Repository: terrapower/armi
Pull request #1912 (2024-09-27 16:15:41)

Alphabetizing Flags.toString results
## What is the change?

``Flags.toString()`` now returns an alphabetized list.

## Why is the change being made?

Previously, this function returned the list of flags (if more than one was provided) in random order, different every time. I think it will be easier for ARMI developers if our code is more predictable.

Also, previous to this PR, the following was always `True` randomly HALF THE TIME, which is insane:

```python
(flags.Flags.A | flags.Flags.B) == (flags.Flags.B | flags.Flags.A)
```

The `or` logical operator is not order dependent in any programming language. And it certainly shouldn't be randomly `True` or `False` based on a coin flip. 

---

## Checklist

- [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`.


Patch:
diff --git a/armi/reactor/flags.py b/armi/reactor/flags.py
index 14bcff7e..3386772f 100644
--- a/armi/reactor/flags.py
+++ b/armi/reactor/flags.py
@@ -185,7 +185,8 @@ def _toString(cls, typeSpec):
     -----
     This converts a flag from ``Flags.A|B`` to ``'A B'``
     """
-    return str(typeSpec).split("{}.".format(cls.__name__))[1].replace("|", " ")
+    strings = str(typeSpec).split("{}.".format(cls.__name__))[1]
+    return " ".join(sorted(strings.split("|")))
 
 
 class Flags(Flag):
diff --git a/doc/release/0.4.rst b/doc/release/0.4.rst
index 45f7dba3..cda3e8ff 100644
--- a/doc/release/0.4.rst
+++ b/doc/release/0.4.rst
@@ -14,17 +14,15 @@ New Features
 #. Allow merging a component with zero area into another component (`PR#1858 <https://github.com/terrapower/armi/pull/1858>`_)
 #. Provide utilities for determining location of a rotated object in a hexagonal lattice (``getIndexOfRotatedCell``). (`PR#1846 <https://github.com/terrapower/armi/1846`)
 #. Allow merging a component with zero area into another component. (`PR#1858 <https://github.com/terrapower/armi/pull/1858>`_)
-#. Provide ``Parameter.hasCategory`` for quickly checking if a parameter is defined with a given category.
-   (`PR#1899 <https://github.com/terrapower/armi/pull/1899>`_)
-#. Provide ``ParameterCollection.where`` for efficient iteration over parameters who's definition.
-   matches a given condition. (`PR#1899 <https://github.com/terrapower/armi/pull/1899>`_)
-#. Plugins can provide the ``getAxialExpansionChanger`` hook to customize axial expansion.
-   (`PR#1870 <https://github.com/terrapower/armi/pull/1870`_)
+#. Provide ``Parameter.hasCategory`` for quickly checking if a parameter is defined with a given category. (`PR#1899 <https://github.com/terrapower/armi/pull/1899>`_)
+#. Provide ``ParameterCollection.where`` for efficient iteration over parameters who's definition matches a given condition. (`PR#1899 <https://github.com/terrapower/armi/pull/1899>`_)
+#. Plugins can provide the ``getAxialExpansionChanger`` hook to customize axial expansion. (`PR#1870 <https://github.com/terrapower/armi/pull/1870`_)
 #. TBD
 
 API Changes
 -----------
 #. Removing flags ``CORE`` and ``REACTOR``. (`PR#1835 <https://github.com/terrapower/armi/pull/1835>`_)
+#. Alphabetizing ``Flags.toString()`` results. (`PR#1912 <https://github.com/terrapower/armi/pull/1912>`_)
 #. Moving ``settingsValidation`` from ``operators`` to ``settings``. (`PR#1895 <https://github.com/terrapower/armi/pull/1895>`_)
 #. Removing deprecated method ``prepSearch``. (`PR#1845 <https://github.com/terrapower/armi/pull/1845>`_)
 #. Removing unused function ``SkippingXsGen_BuChangedLessThanTolerance``. (`PR#1845 <https://github.com/terrapower/armi/pull/1845>`_)
@@ -42,8 +40,7 @@ Bug Fixes
 #. Changed data type of ``thKernel`` setting from ``bool`` to ``str`` in ``ThermalHydraulicsPlugin``. (`PR#1855 <https://github.com/terrapower/armi/pull/1855>`_)
 #. Rotate hexagonal assembly patches correctly on facemap plots. (`PR#1883 <https://github.com/terrapower/armi/pull/1883>`_)
 #. Update height of fluid components after axial expansion (`PR#1828 <https://github.com/terrapower/armi/pull/1828>`_)
-#. Material theoretical density is serialized to and read from database.
-   (`PR#1852 <https://github.com/terrapower/armi/pull/1852>`_)
+#. Material theoretical density is serialized to and read from database. (`PR#1852 <https://github.com/terrapower/armi/pull/1852>`_)
 #. TBD
 
 Quality Work


Test patch:
diff --git a/armi/reactor/tests/test_flags.py b/armi/reactor/tests/test_flags.py
index bbc4c1cb..7155cae9 100644
--- a/armi/reactor/tests/test_flags.py
+++ b/armi/reactor/tests/test_flags.py
@@ -38,6 +38,14 @@ class TestFlags(unittest.TestCase):
         self.assertEqual(flags.Flags.toString(f), "FUEL")
         self.assertEqual(f, flags.Flags.fromString("FUEL"))
 
+    def test_toStringAlphabetical(self):
+        """Ensure that, for multiple flags, toString() returns them in alphabetical order."""
+        flagz = flags.Flags.AXIAL | flags.Flags.LOWER
+        self.assertEqual(flags.Flags.toString(flagz), "AXIAL LOWER")
+
+        flagz = flags.Flags.LOWER | flags.Flags.AXIAL
+        self.assertEqual(flags.Flags.toString(flagz), "AXIAL LOWER")
+
     def test_fromStringStrict(self):
         self._help_fromString(flags.Flags.fromString)
         with self.assertRaises(flags.InvalidFlagsError):
diff --git a/armi/tests/ThRZSettings.yaml b/armi/tests/ThRZSettings.yaml
index 2fee7d11..f9ef807a 100644
--- a/armi/tests/ThRZSettings.yaml
+++ b/armi/tests/ThRZSettings.yaml
@@ -1,14 +1,19 @@
 metadata:
   version: uncontrolled
 settings:
+# global
   branchVerbosity: info
   burnSteps: 0
   comment: "Revised benchmark  "
-  db: false
-  genXS: Neutron
   geomFile: ThRZGeom.xml
-  groupStructure: ARMI45
   loadingFile: ThRZloading.yaml
   numProcessors: 12
   outputFileExtension: png
   power: 1000000.0
+
+# database
+  db: false
+
+# neutronics
+  genXS: Neutron
+  groupStructure: ARMI45
diff --git a/armi/tests/armiRun.yaml b/armi/tests/armiRun.yaml
index 8c170247..c8b8d2e0 100644
--- a/armi/tests/armiRun.yaml
+++ b/armi/tests/armiRun.yaml
@@ -1,4 +1,5 @@
 settings:
+# global
   availabilityFactor: 1
   beta: 0.003454
   branchVerbosity: debug
@@ -6,6 +7,25 @@ settings:
     - 100
   burnSteps: 2
   comment: Simple test input.
+  cycleLength: 2000.0
+  detailAssemLocationsBOL:
+    - 002-001
+  freshFeedType: igniter fuel
+  loadingFile: refSmallReactor.yaml
+  moduleVerbosity:
+    armi.reactor.reactors: info
+  nCycles: 6
+  outputFileExtension: png
+  power: 100000000.0
+  smallRun: true
+  startCycle: 1
+  startNode: 2
+  targetK: 1.002
+  verbosity: extra
+  versions:
+    armi: uncontrolled
+
+# cross section
   crossSectionControl:
     DA:
       geometry: 0D
@@ -31,30 +51,20 @@ settings:
         - gap
       numInternalRings: 1
       numExternalRings: 1
-  cycleLength: 2000.0
+
+# database
   db: false
-  detailAssemLocationsBOL:
-    - 002-001
-  epsBurnTime: 0.001
-  epsFSAvg: 1e-06
-  epsFSPoint: 1e-06
-  freshFeedType: igniter fuel
+
+# fuel cycle
   fuelHandlerName: EquilibriumShuffler
   jumpRingNum: 9
-  loadingFile: refSmallReactor.yaml
-  startCycle: 1
-  startNode: 2
+  shuffleLogic: refSmallReactorShuffleLogic.py
+
+# neutronics
+  epsFSAvg: 1e-06
+  epsFSPoint: 1e-06
   loadPadElevation: 200.0
+
+# report
   genReports: false
-  moduleVerbosity:
-    armi.reactor.reactors: info
-  nCycles: 6
-  outputFileExtension: png
-  power: 100000000.0
-  shuffleLogic: refSmallReactorShuffleLogic.py
-  smallRun: true
   summarizeAssemDesign: false
-  targetK: 1.002
-  verbosity: extra
-  versions:
-    armi: uncontrolled
diff --git a/armi/tests/detailedAxialExpansion/armiRun.yaml b/armi/tests/detailedAxialExpansion/armiRun.yaml
index feda7a6c..c3d57f79 100644
--- a/armi/tests/detailedAxialExpansion/armiRun.yaml
+++ b/armi/tests/detailedAxialExpansion/armiRun.yaml
@@ -1,12 +1,29 @@
 settings:
-  axialExpansion: true
-  detailedAxialExpansion: true
+# global
   beta: 0.003454
   branchVerbosity: debug
   buGroups:
     - 100
   burnSteps: 2
   comment: Simple test input with detailed axial expansion.
+  cycleLength: 2000.0
+  detailAssemLocationsBOL:
+    - 002-001
+  detailedAxialExpansion: true
+  freshFeedType: igniter fuel
+  loadingFile: refSmallReactor.yaml
+  moduleVerbosity:
+    armi.reactor.reactors: info
+  nCycles: 6
+  outputFileExtension: png
+  power: 100000000.0
+  startNode: 1
+  targetK: 1.002
+  verbosity: extra
+  versions:
+    armi: uncontrolled
+
+# cross section
   crossSectionControl:
     DA:
       geometry: 0D
@@ -19,26 +36,22 @@ settings:
     XA:
       xsFileLocation:
         - ISOXA
-  cycleLength: 2000.0
+
+# database
   db: false
-  detailAssemLocationsBOL:
-    - 002-001
-  epsFSAvg: 1e-06
-  epsFSPoint: 1e-06
-  freshFeedType: igniter fuel
+
+# fuel cycle
   fuelHandlerName: EquilibriumShuffler
   jumpRingNum: 9
-  loadingFile: refSmallReactor.yaml
-  startNode: 1
+
+# fuel performance
+  axialExpansion: true
+
+# neutronics
+  epsFSAvg: 1e-06
+  epsFSPoint: 1e-06
   loadPadElevation: 162.5
+
+# report
   genReports: false
-  moduleVerbosity:
-    armi.reactor.reactors: info
-  nCycles: 6
-  outputFileExtension: png
-  power: 100000000.0
   summarizeAssemDesign: false
-  targetK: 1.002
-  verbosity: extra
-  versions:
-    armi: uncontrolled
diff --git a/armi/tests/refTestCartesian.yaml b/armi/tests/refTestCartesian.yaml
index a4868d64..dc166ade 100644
--- a/armi/tests/refTestCartesian.yaml
+++ b/armi/tests/refTestCartesian.yaml
@@ -1,21 +1,27 @@
 settings:
+# global
   beta: 0.003454
   buGroups:
     - 100
   burnSteps: 0
   comment: Full-core Cartesian input file with a 10x10 cm square pitch.
   cycleLength: 2000.0
-  epsBurnTime: 0.001
-  epsFSAvg: 1e-06
-  epsFSPoint: 1e-06
   freshFeedType: igniter fuel
-  jumpRingNum: 9
   loadingFile: refSmallCartesian.yaml
-  startNode: 1
-  loadPadElevation: 200.0
   outputFileExtension: png
   power: 400000000.0
-  summarizeAssemDesign: false
+  startNode: 1
   targetK: 1.002
   versions:
     armi: uncontrolled
+
+# fuel cycle
+  jumpRingNum: 9
+
+# neutronics
+  epsFSAvg: 1e-06
+  epsFSPoint: 1e-06
+  loadPadElevation: 200.0
+
+# report
+  summarizeAssemDesign: false
diff --git a/armi/tests/smallestTestReactor/armiRunSmallest.yaml b/armi/tests/smallestTestReactor/armiRunSmallest.yaml
index 83eccc81..544bc7b1 100644
--- a/armi/tests/smallestTestReactor/armiRunSmallest.yaml
+++ b/armi/tests/smallestTestReactor/armiRunSmallest.yaml
@@ -3,6 +3,7 @@
 # This is a single-hex-assembly reactor, with only one block.
 
 settings:
+# global
   availabilityFactor: 1
   beta: 0.003454
   branchVerbosity: debug
@@ -10,6 +11,25 @@ settings:
     - 100
   burnSteps: 2
   comment: Simple test input.
+  cycleLength: 2000.0
+  detailAssemLocationsBOL:
+    - 002-001
+  freshFeedType: igniter fuel
+  loadingFile: refSmallestReactor.yaml
+  moduleVerbosity:
+    armi.reactor.reactors: info
+  nCycles: 2
+  outputFileExtension: png
+  power: 1000000.0
+  smallRun: true
+  startCycle: 1
+  startNode: 2
+  targetK: 1.002
+  verbosity: extra
+  versions:
+    armi: uncontrolled
+
+# cross section
   crossSectionControl:
     DA:
       geometry: 0D
@@ -35,28 +55,19 @@ settings:
         - gap
       numInternalRings: 1
       numExternalRings: 1
-  cycleLength: 2000.0
+
+# database
   db: false
-  detailAssemLocationsBOL:
-    - 002-001
-  epsFSAvg: 1e-06
-  epsFSPoint: 1e-06
-  freshFeedType: igniter fuel
+
+# fuel cycle
   fuelHandlerName: EquilibriumShuffler
   jumpRingNum: 9
-  loadingFile: refSmallestReactor.yaml
-  startCycle: 1
-  startNode: 2
+
+# neutronics
+  epsFSAvg: 1e-06
+  epsFSPoint: 1e-06
   loadPadElevation: 200.0
+
+# report
   genReports: false
-  moduleVerbosity:
-    armi.reactor.reactors: info
-  nCycles: 2
-  outputFileExtension: png
-  power: 1000000.0
-  smallRun: true
   summarizeAssemDesign: false
-  targetK: 1.002
-  verbosity: extra
-  versions:
-    armi: uncontrolled
diff --git a/armi/tests/test_lwrInputs.py b/armi/tests/test_lwrInputs.py
index 4b40afd8..1207ea47 100644
--- a/armi/tests/test_lwrInputs.py
+++ b/armi/tests/test_lwrInputs.py
@@ -63,7 +63,6 @@ class C5G7ReactorTests(unittest.TestCase):
             streamVal = mock.getStdout()
             self.assertIn("UraniumOxide", streamVal, msg=streamVal)
             self.assertIn("SaturatedWater", streamVal, msg=streamVal)
-            self.assertIn("invalid settings: fakeBad", streamVal, msg=streamVal)
 
             # test that there are 100 of each high, medium, and low MOX pins
             fuelPinsHigh = b.getComponent(Flags.HIGH | Flags.MOX)
diff --git a/armi/tests/tutorials/c5g7-settings.yaml b/armi/tests/tutorials/c5g7-settings.yaml
index 9abc3e90..815c246a 100644
--- a/armi/tests/tutorials/c5g7-settings.yaml
+++ b/armi/tests/tutorials/c5g7-settings.yaml
@@ -1,16 +1,20 @@
 settings:
+# global
   availabilityFactor: 0.9
-  fakeBad: 123456
-  power: 1000000000.0
-  cycleLength: 411.11
-  loadingFile: c5g7-blueprints.yaml
-  nCycles: 10
-  burnSteps: 2
   buGroups:
     - 100
+  burnSteps: 2
   comment: C5G7 LWR Benchmark inputs
-  genXS: Neutron
+  cycleLength: 411.11
+  loadingFile: c5g7-blueprints.yaml
+  nCycles: 10
   numProcessors: 1
-  genReports: false
+  power: 1000000000.0
   versions:
     armi: uncontrolled
+
+# neutronics
+  genXS: Neutron
+
+# report
+  genReports: false
diff --git a/armi/tests/zpprTest.yaml b/armi/tests/zpprTest.yaml
index 66094e64..51a6bb81 100644
--- a/armi/tests/zpprTest.yaml
+++ b/armi/tests/zpprTest.yaml
@@ -1,10 +1,24 @@
 metadata:
   version: uncontrolled
 settings:
+# global
+  Tin: 20.0
+  Tout: 20.0
   buGroups:
     - 100
   burnSteps: 0
   comment: ZPPR test case
+  cycleLength: 365.25
+  geomFile: zpprTestGeom.xml
+  loadingFile: 1DslabXSByCompTest.yaml
+  mpiTasksPerNode: 6
+  numProcessors: 12
+  outputFileExtension: pdf
+  power: 75000000.0
+  sortReactor: false # zpprs dont sor the right way. need better component sorting for slab...
+  verbosity: extra
+
+# cross section
   crossSectionControl:
     AA:
       geometry: 1D slab
@@ -27,17 +41,8 @@ settings:
       numInternalRings: 1
       numExternalRings: 1
       meshSubdivisionsPerCm: 10
-  cycleLength: 365.25
+
+# neutronics
   epsEig: 1e-10
   genXS: Neutron
-  geomFile: zpprTestGeom.xml
-  loadingFile: 1DslabXSByCompTest.yaml
-  mpiTasksPerNode: 6
-  numProcessors: 12
-  outputFileExtension: pdf
-  power: 75000000.0
-  sortReactor: false # zpprs dont sor the right way. need better component sorting for slab...
-  Tin: 20.0
-  Tout: 20.0
-  verbosity: extra
   xsBlockRepresentation: ComponentAverage1DSlab