Repository: terrapower/armi
Pull request #1751 (2024-06-26 19:47:47)

Clean up a minor bug in #1653
## What is the change?

Change logic in `CrossSectionGroupManager` to handle the case where `BlockCollection._validRepresentativeBlockTypes` is `None`. 

## Why is the change being made?

This logic was introduced in PR #1653. The logic checks for the length of `_validRepresentativeBlockTypes`, assuming it is always a `list`. It did not consider the case where `_validRepresentativeBlockTypes` is None.

---

## Checklist

<!--
    You (the pull requester) should put an `x` in the boxes below you have completed.

    (If a checkbox doesn't apply to your PR, check it anyway.)
-->

- [x] This PR has only [one purpose or idea](https://terrapower.github.io/armi/developer/tooling.html#one-idea-one-pr).
- [x] [Tests](https://terrapower.github.io/armi/developer/tooling.html#test-it) have been added/updated to verify any new/changed code.

<!-- Check the code quality -->

- [x] The code style follows [good practices](https://terrapower.github.io/armi/developer/standards_and_practices.html).
- [x] The commit message(s) follow [good practices](https://terrapower.github.io/armi/developer/tooling.html).

<!-- Check the project-level cruft -->

- [x] The [release notes](https://terrapower.github.io/armi/developer/tooling.html#add-release-notes) have been updated if necessary.
- [x] The [documentation](https://terrapower.github.io/armi/developer/tooling.html#document-it) is still up-to-date in the `doc` folder.
- [x] The dependencies are still up-to-date in `pyproject.toml`.

mgjarrett: @john-science this PR addresses a corner case bug in PR #1653 that wasn't caught until after it was merged.

Patch:
diff --git a/armi/operators/operator.py b/armi/operators/operator.py
index ac98fa1b..10da03c4 100644
--- a/armi/operators/operator.py
+++ b/armi/operators/operator.py
@@ -1077,6 +1077,8 @@ class Operator:
         """
         if self.r:
             self.r.o = None
+            for comp in self.r:
+                comp.parent = None
         self.r = None
         for i in self.interfaces:
             i.o = None
diff --git a/armi/operators/operatorMPI.py b/armi/operators/operatorMPI.py
index 2f54c75e..bb976165 100644
--- a/armi/operators/operatorMPI.py
+++ b/armi/operators/operatorMPI.py
@@ -238,6 +238,7 @@ class OperatorMPI(Operator):
         cs = self.cs
         bp = self.r.blueprints
         spatialGrid = self.r.core.spatialGrid
+        spatialGrid.armiObject = None
         xsGroups = self.getInterface("xsGroups")
         if xsGroups:
             xsGroups.clearRepresentativeBlocks()
@@ -246,6 +247,7 @@ class OperatorMPI(Operator):
         core = reactors.Core("Core")
         self.r.add(core)
         core.spatialGrid = spatialGrid
+        core.spatialGrid.armiObject = core
         self.reattach(self.r, cs)
 
     @staticmethod
diff --git a/armi/physics/neutronics/crossSectionGroupManager.py b/armi/physics/neutronics/crossSectionGroupManager.py
index c0cfb808..2875f132 100644
--- a/armi/physics/neutronics/crossSectionGroupManager.py
+++ b/armi/physics/neutronics/crossSectionGroupManager.py
@@ -1235,12 +1235,12 @@ class CrossSectionGroupManager(interfaces.Interface):
 
             # create a new block collection that inherits all of the properties
             # and settings from oldBlockCollection.
-            if len(oldBlockCollection._validRepresentativeBlockTypes) > 0:
-                validBlockTypes = []
-                for flag in oldBlockCollection._validRepresentativeBlockTypes:
-                    validBlockTypes.append(flags._toString(Flags, flag))
-            else:
-                validBlockTypes = None
+            validBlockTypes = oldBlockCollection._validRepresentativeBlockTypes
+            if validBlockTypes is not None and len(validBlockTypes) > 0:
+                validBlockTypes = [
+                    flags._toString(Flags, flag)
+                    for flag in oldBlockCollection._validRepresentativeBlockTypes
+                ]
             newBlockCollection = oldBlockCollection.__class__(
                 oldBlockCollection.allNuclidesInProblem,
                 validBlockTypes=validBlockTypes,
diff --git a/doc/release/0.3.rst b/doc/release/0.3.rst
index 47385f6d..d857ac35 100644
--- a/doc/release/0.3.rst
+++ b/doc/release/0.3.rst
@@ -11,6 +11,8 @@ New Features
 #. Conserve mass by component in ``assembly.setBlockMesh()``. (`PR#1665 <https://github.com/terrapower/armi/pull/1665>`_)
 #. Removal of the ``Block.reactor`` property. (`PR#1425 <https://github.com/terrapower/armi/pull/1425>`_)
 #. System information is now also logged on Linux. (`PR#1689 <https://github.com/terrapower/armi/pull/1689>`_)
+#. Reset ``Reactor`` data on worker processors after every interaction to free memory from state distribution.
+   (`PR#1729 <https://github.com/terrapower/armi/pull/1729>`_ and `PR#1750 <https://github.com/terrapower/armi/pull/1750>`_)
 #. TBD
 
 API Changes
@@ -53,7 +55,7 @@ Bug Fixes
 ---------
 #. Fixed four bugs with "corners up" hex grids. (`PR#1649 <https://github.com/terrapower/armi/pull/1649>`_)
 #. Fixed ``safeCopy`` to work on both Windows and Linux with strict permissions (`PR#1691 <https://github.com/terrapower/armi/pull/1691>`_)
-#. When creating a new XS group, inherit settings from initial group. (`PR#1653 <https://github.com/terrapower/armi/pull/1653>`_)
+#. When creating a new XS group, inherit settings from initial group. (`PR#1653 <https://github.com/terrapower/armi/pull/1653>`_, `PR#1751 <https://github.com/terrapower/armi/pull/1751>`_)
 
 #. TBD
 


Test patch:
diff --git a/armi/physics/neutronics/tests/test_crossSectionManager.py b/armi/physics/neutronics/tests/test_crossSectionManager.py
index 9e857975..2437420a 100644
--- a/armi/physics/neutronics/tests/test_crossSectionManager.py
+++ b/armi/physics/neutronics/tests/test_crossSectionManager.py
@@ -816,16 +816,8 @@ class TestCrossSectionGroupManager(unittest.TestCase):
         self.assertIsNone(blocks[0].p.detailedNDens)
         self.assertIsNone(blocks[1].p.detailedNDens)
 
-    def test_createRepresentativeBlocksUsingExistingBlocks(self):
-        """
-        Demonstrates that a new representative block can be generated from an existing
-        representative block.
-
-        Notes
-        -----
-        This tests that the XS ID of the new representative block is correct and that the
-        compositions are identical between the original and the new representative blocks.
-        """
+    def _createRepresentativeBlocksUsingExistingBlocks(self, validBlockTypes):
+        """Reusable code used in multiple unit tests."""
         o, r = test_reactors.loadTestReactor(
             TEST_ROOT, inputFileName="smallestTestReactor/armiRunSmallest.yaml"
         )
@@ -843,7 +835,7 @@ class TestCrossSectionGroupManager(unittest.TestCase):
             }
         )
         o.cs[CONF_CROSS_SECTION].setDefaults(
-            crossSectionGroupManager.AVERAGE_BLOCK_COLLECTION, ["fuel"]
+            crossSectionGroupManager.AVERAGE_BLOCK_COLLECTION, validBlockTypes
         )
         aaSettings = o.cs[CONF_CROSS_SECTION]["AA"]
         self.csm.cs = copy.deepcopy(o.cs)
@@ -877,6 +869,30 @@ class TestCrossSectionGroupManager(unittest.TestCase):
                 continue
             self.assertEqual(baSettingValue, aaSettings.__dict__[setting])
 
+    def test_createRepresentativeBlocksUsingExistingBlocks(self):
+        """
+        Demonstrates that a new representative block can be generated from an existing
+        representative block.
+
+        Notes
+        -----
+        This tests that the XS ID of the new representative block is correct and that the
+        compositions are identical between the original and the new representative blocks.
+        """
+        self._createRepresentativeBlocksUsingExistingBlocks(["fuel"])
+
+    def test_createRepresentativeBlocksUsingExistingBlocksDisableValidBlockTypes(self):
+        """
+        Demonstrates that a new representative block can be generated from an existing
+        representative block with the setting `disableBlockTypeExclusionInXsGeneration: true`.
+
+        Notes
+        -----
+        This tests that the XS ID of the new representative block is correct and that the
+        compositions are identical between the original and the new representative blocks.
+        """
+        self._createRepresentativeBlocksUsingExistingBlocks(True)
+
     def test_interactBOL(self):
         """Test `BOL` lattice physics update frequency.