Repository: terrapower/armi

Issue:
Add `--skip-inspection` flag to `compare-suites` CLI
Most of the time we don't want this. So let's allow folks to skip the inspection

OR! Make that the default!!!! And make a `--do-inspection` flag 😀

Discussion:

Patch:
diff --git a/armi/cases/suite.py b/armi/cases/suite.py
index 7733c0b2..222e2397 100644
--- a/armi/cases/suite.py
+++ b/armi/cases/suite.py
@@ -86,7 +86,12 @@ class CaseSuite:
         return len(self._cases)
 
     def discover(
-        self, rootDir=None, patterns=None, ignorePatterns=None, recursive=True
+        self,
+        rootDir=None,
+        patterns=None,
+        ignorePatterns=None,
+        recursive=True,
+        skipInspection=False,
     ):
         """
         Finds case objects by searching for a pattern of file paths, and adds them to
@@ -104,6 +109,8 @@ class CaseSuite:
             file patterns to exclude matching file names
         recursive : bool, optional
             if True, recursively search for settings files
+        skipInspection : bool, optional
+            if True, skip running the check inputs
         """
         csFiles = settings.recursivelyLoadSettingsFiles(
             rootDir or os.path.abspath(os.getcwd()),
@@ -115,7 +122,8 @@ class CaseSuite:
 
         for cs in csFiles:
             case = armicase.Case(cs=cs, caseSuite=self)
-            case.checkInputs()
+            if not skipInspection:
+                case.checkInputs()
             self.add(case)
 
     def echoConfiguration(self):
diff --git a/armi/cli/compareCases.py b/armi/cli/compareCases.py
index b42e97f9..cd5acd15 100644
--- a/armi/cli/compareCases.py
+++ b/armi/cli/compareCases.py
@@ -162,6 +162,15 @@ class CompareSuites(CompareCases):
             default=[],
             help="Pattern to search for inputs to ignore.",
         )
+        self.parser.add_argument(
+            "--skip-inspection",
+            "-I",
+            action="store_true",
+            default=False,
+            help="Skip inspection. By default, setting files are checked for integrity and consistency. These "
+            "checks result in needing to manually resolve a number of differences. Using this option will "
+            "suppress the inspection step.",
+        )
 
     def invoke(self):
         from armi import cases
@@ -188,6 +197,7 @@ class CompareSuites(CompareCases):
             rootDir=self.args.reference,
             patterns=allTests,
             ignorePatterns=self.args.ignore,
+            skipInspection=self.args.skip_inspection,
         )
 
         cmpSuite = cases.CaseSuite(self.cs)
@@ -195,6 +205,7 @@ class CompareSuites(CompareCases):
             rootDir=self.args.comparison,
             patterns=self.args.patterns,
             ignorePatterns=self.args.ignore,
+            skipInspection=self.args.skip_inspection,
         )
 
         nIssues = refSuite.compare(
@@ -206,4 +217,4 @@ class CompareSuites(CompareCases):
         )
 
         if nIssues > 0:
-            sys.exit(nIssues)
+            sys.exit(1)
diff --git a/armi/reactor/composites.py b/armi/reactor/composites.py
index 9a4a9c3d..91b53b82 100644
--- a/armi/reactor/composites.py
+++ b/armi/reactor/composites.py
@@ -51,6 +51,7 @@ from armi.utils import densityTools
 from armi.utils import tabulate
 from armi.utils import units
 from armi.utils.densityTools import calculateNumberDensity
+from armi.utils.flags import auto
 
 
 class FlagSerializer(parameters.Serializer):
@@ -68,7 +69,7 @@ class FlagSerializer(parameters.Serializer):
     @staticmethod
     def pack(data):
         """
-        Flags are represented as a 2-D numpy array of uint8 (single-byte, unsigned
+        Flags are represented as a 2D numpy array of uint8 (single-byte, unsigned
         integers), where each row contains the bytes representing a single Flags
         instance. We also store the list of field names so that we can verify that the
         reader and the writer can agree on the meaning of each bit.
@@ -103,7 +104,6 @@ class FlagSerializer(parameters.Serializer):
         ----------
         inp : int
             input bitfield
-
         mapping : dict
             dictionary mapping from old bit position -> new bit position
         """
@@ -168,11 +168,14 @@ class FlagSerializer(parameters.Serializer):
         # Make sure that all of the old flags still exist
         if not flagSetIn.issubset(flagSetNow):
             missingFlags = flagSetIn - flagSetNow
-            raise ValueError(
-                "The set of flags in the database includes unknown flags. "
-                "Make sure you are using the correct ARMI app. Missing flags:\n"
-                "{}".format(missingFlags)
+            runLog.warning(
+                "The set of flags in the database includes unknown flags. For convenience, we will "
+                f"add these to the system: {missingFlags}"
             )
+            flagCls.extend({k: auto() for k in missingFlags})
+
+        flagOrderNow = flagCls.sortedFields()
+        flagSetNow = set(flagOrderNow)
 
         if all(i == j for i, j in zip(flagOrderPassed, flagOrderNow)):
             out = [flagCls.from_bytes(row.tobytes()) for row in data]
diff --git a/doc/release/0.4.rst b/doc/release/0.4.rst
index 69169c07..c61c6079 100644
--- a/doc/release/0.4.rst
+++ b/doc/release/0.4.rst
@@ -10,11 +10,13 @@ New Features
 ------------
 #. ARMI now supports Python 3.12. (`PR#1813 <https://github.com/terrapower/armi/pull/1813>`_)
 #. Removing the ``tabulate`` dependency by ingesting it to ``armi.utils.tabulate``. (`PR#1811 <https://github.com/terrapower/armi/pull/1811>`_)
+#. Adding ``--skip-inspection`` flag to ``CompareCases`` CLI. (`PR#1842 <https://github.com/terrapower/armi/pull/1842>`_)
 #. TBD
 
 API Changes
 -----------
 #. Removing flags ``CORE`` and ``REACTOR``. (`PR#1835 <https://github.com/terrapower/armi/pull/1835>`_)
+#. Allow for unknown Flags when opening a DB. (`PR#1844 <https://github.com/terrapower/armi/pull/1835>`_)
 #. TBD
 
 Bug Fixes
@@ -25,7 +27,7 @@ Bug Fixes
 
 Quality Work
 ------------
-#. Removing code that causes a deprecation warning (converting ``axialUnitGrid`` to ``AxialGrid.fromNCells``) (`PR#1809 <https://github.com/terrapower/armi/pull/1809>`_)
+#. Removing deprecated code (``axialUnitGrid``) (`PR#1809 <https://github.com/terrapower/armi/pull/1809>`_)
 #. TBD
 
 Changes that Affect Requirements


Test patch:
diff --git a/armi/cli/tests/test_runEntryPoint.py b/armi/cli/tests/test_runEntryPoint.py
index 370200e2..b0e541d6 100644
--- a/armi/cli/tests/test_runEntryPoint.py
+++ b/armi/cli/tests/test_runEntryPoint.py
@@ -241,10 +241,11 @@ class TestCompareSuites(unittest.TestCase):
         with TemporaryDirectoryChanger():
             cs = CompareSuites()
             cs.addOptions()
-            cs.parse_args(["/path/to/fake1.h5", "/path/to/fake2.h5"])
+            cs.parse_args(["/path/to/fake1.h5", "/path/to/fake2.h5", "-I"])
 
             self.assertEqual(cs.name, "compare-suites")
             self.assertEqual(cs.args.reference, "/path/to/fake1.h5")
+            self.assertTrue(cs.args.skip_inspection)
             self.assertIsNone(cs.args.weights)
 
 
diff --git a/armi/reactor/tests/test_composites.py b/armi/reactor/tests/test_composites.py
index 85f308e4..5a60cc5a 100644
--- a/armi/reactor/tests/test_composites.py
+++ b/armi/reactor/tests/test_composites.py
@@ -14,6 +14,7 @@
 
 """Tests for the composite pattern."""
 from copy import deepcopy
+import logging
 import unittest
 
 from armi import nuclearDataIO
@@ -36,6 +37,7 @@ from armi.reactor.flags import Flags, TypeSpec
 from armi.reactor.tests.test_blocks import loadTestBlock
 from armi.reactor.tests.test_reactors import loadTestReactor
 from armi.tests import ISOAA_PATH
+from armi.tests import mockRunLogs
 
 
 class MockBP:
@@ -692,10 +694,19 @@ class TestFlagSerializer(unittest.TestCase):
 
         # missing flags in current version Flags
         attrs["flag_order"].append("NONEXISTANTFLAG")
-        with self.assertRaises(ValueError):
+        with mockRunLogs.BufferLog() as mock:
+            self.assertEqual("", mock.getStdout())
+            testName = "test_flagSerialization"
+            runLog.LOG.startLog(testName)
+            runLog.LOG.setVerbosity(logging.WARNING)
+
             data2 = composites.FlagSerializer.unpack(
                 flagsArray, composites.FlagSerializer.version, attrs
             )
+            flagLog = mock.getStdout()
+
+        self.assertIn("The set of flags", flagLog)
+        self.assertIn("NONEXISTANTFLAG", flagLog)
 
     def test_flagConversion(self):
         data = [