Repository: terrapower/armi
Pull request #1989 (2024-10-30 20:25:48)

Adding setting to control MCNP / ENDF library
## What is the change?

Adding setting `mcnpLibraryBaseName` to control ENDF/B library name for MCNP.

## Why is the change being made?

To make it easier to control the elements we use to model.

---

## 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/physics/neutronics/settings.py b/armi/physics/neutronics/settings.py
index f9e23cab..d6aaa42b 100644
--- a/armi/physics/neutronics/settings.py
+++ b/armi/physics/neutronics/settings.py
@@ -43,6 +43,7 @@ CONF_GROUP_STRUCTURE = "groupStructure"
 CONF_INNERS_ = "inners"
 CONF_LOADING_FILE = "loadingFile"
 CONF_NEUTRONICS_KERNEL = "neutronicsKernel"
+CONF_MCNP_LIB_BASE = "mcnpLibraryVersion"
 CONF_NEUTRONICS_TYPE = "neutronicsType"
 CONF_NUMBER_MESH_PER_EDGE = "numberMeshPerEdge"
 CONF_OUTERS_ = "outers"
@@ -172,6 +173,13 @@ def defineSettings():
             options=[],
             enforcedOptions=True,
         ),
+        setting.Setting(
+            CONF_MCNP_LIB_BASE,
+            default="ENDF/B-VII.1",
+            description="Library name for MCNP cross sections. ENDF/B-VII.1 is the default library.",
+            label="ENDF data library version to use for MCNP Analysis",
+            options=["ENDF/B-V.0", "ENDF/B-VII.0", "ENDF/B-VII.1", "ENDF/B-VIII.0"],
+        ),
         setting.Setting(
             CONF_NEUTRONICS_TYPE,
             default="real",
diff --git a/armi/reactor/blocks.py b/armi/reactor/blocks.py
index a3bf12ea..27e91a8f 100644
--- a/armi/reactor/blocks.py
+++ b/armi/reactor/blocks.py
@@ -1992,12 +1992,12 @@ class HexBlock(Block):
 
     def rotate(self, rad: float):
         """
-        Rotates a block's spatially-varying parameters by a specified angle in the counter-clockwise
+        Rotates a block's spatially varying parameters by a specified angle in the counter-clockwise
         direction.
 
-        The parameters must have a ParamLocation of either CORNERS or EDGES and must be a list of
-        length 6 in order to be eligible for rotation; all parameters that do not meet these two
-        criteria are not rotated.
+        The parameters must have a ParamLocation of either CORNERS or EDGES and must be a Python
+        list of length 6 in order to be eligible for rotation; all parameters that do not meet these
+        two criteria are not rotated.
 
         .. impl:: Rotating a hex block updates parameters on the boundary, the orientation
             parameter, and the spatial coordinates on contained objects.
@@ -2006,9 +2006,9 @@ class HexBlock(Block):
 
         Parameters
         ----------
-        rad: float
+        rad: float, required
             Angle of counter-clockwise rotation in units of radians. Rotations must be in 60-degree
-            increments (i.e., PI/6, PI/3, PI, 2 * PI/3, 5 * PI/6, and 2 * PI).
+            increments (i.e., PI/6, PI/3, PI, 2 * PI/3, 5 * PI/6, and 2 * PI)
         """
         rotNum = round((rad % (2 * math.pi)) / math.radians(60))
         self._rotateChildLocations(rad, rotNum)
diff --git a/armi/reactor/blueprints/isotopicOptions.py b/armi/reactor/blueprints/isotopicOptions.py
index 506be7b5..37ded762 100644
--- a/armi/reactor/blueprints/isotopicOptions.py
+++ b/armi/reactor/blueprints/isotopicOptions.py
@@ -33,6 +33,7 @@ from armi.physics.neutronics.fissionProductModel.fissionProductModelSettings imp
     CONF_FISSION_PRODUCT_LIBRARY_NAME,
 )
 from armi.physics.neutronics.settings import (
+    CONF_MCNP_LIB_BASE,
     CONF_NEUTRONICS_KERNEL,
     CONF_XS_KERNEL,
 )
@@ -456,7 +457,6 @@ def getDefaultNuclideFlags():
     We will include B10 and B11 without depletion, sodium, and structural elements.
 
     We will include LFPs with depletion.
-
     """
     nuclideFlags = {}
     actinides = {
@@ -511,7 +511,6 @@ def eleExpandInfoBasedOnCodeENDF(cs):
         For example: {oxygen: [oxygen16]} indicates that all
         oxygen should be expanded to O16, ignoring natural
         O17 and O18. (variables are Natural/NuclideBases)
-
     """
     elementalsToKeep = set()
     oxygenElementals = [nuclideBases.byName["O"]]
@@ -536,20 +535,22 @@ def eleExpandInfoBasedOnCodeENDF(cs):
 
     if "MCNP" in cs[CONF_NEUTRONICS_KERNEL]:
         expansionStrings.update(mcnpExpansions)
-        if int(cs["mcnpLibrary"]) == 50:
+        if cs[CONF_MCNP_LIB_BASE] == "ENDF/B-V.0":
+            # ENDF/B V.0
             elementalsToKeep.update(nuclideBases.instances)  # skip expansion
-        # ENDF/B VII.0
-        elif 70 <= int(cs["mcnpLibrary"]) <= 79:
+        elif cs[CONF_MCNP_LIB_BASE] == "ENDF/B-VII.0":
+            # ENDF/B VII.0
             elementalsToKeep.update(endf70Elementals)
-        # ENDF/B VII.1
-        elif 80 <= int(cs["mcnpLibrary"]) <= 89:
+        elif cs[CONF_MCNP_LIB_BASE] == "ENDF/B-VII.1":
+            # ENDF/B VII.1
             elementalsToKeep.update(endf71Elementals)
+        elif cs[CONF_MCNP_LIB_BASE] == "ENDF/B-VIII.0":
+            # ENDF/B VIII.0
+            elementalsToKeep.update(endf80Elementals)
         else:
             raise InputError(
-                "Failed to determine nuclides for modeling. "
-                "The `mcnpLibrary` setting value ({}) is not supported.".format(
-                    cs["mcnpLibrary"]
-                )
+                "Failed to determine nuclides for modeling. The `mcnpLibraryVersion` "
+                f"setting value ({cs[CONF_MCNP_LIB_BASE]}) is not supported."
             )
 
     elif cs[CONF_XS_KERNEL] == "SERPENT":
diff --git a/doc/release/0.5.rst b/doc/release/0.5.rst
index 8ae4f1a9..57a77421 100644
--- a/doc/release/0.5.rst
+++ b/doc/release/0.5.rst
@@ -34,6 +34,7 @@ New Features
 #. Adding data models for ex-core structures in ARMI. (`PR#1891 <https://github.com/terrapower/armi/pull/1891>`_)
 #. Opening some DBs without the ``App`` that created them. (`PR#1917 <https://github.com/terrapower/armi/pull/1917>`_)
 #. Adding support for ENDF/B-VII.1-based MC2-3 libraries. (`PR#1982 <https://github.com/terrapower/armi/pull/1982>`_)
+#. Adding setting ``mcnpLibraryVersion`` to chosen ENDF library for MCNP. (`PR#1989 <https://github.com/terrapower/armi/pull/1989>`_)
 #. Removing the ``tabulate`` dependency by ingesting it to ``armi.utils.tabulate``. (`PR#1811 <https://github.com/terrapower/armi/pull/1811>`_)
 #. ``HexBlock.rotate`` updates the spatial locator for children of that block. (`PR#1943 <https://github.com/terrapower/armi/pull/1943>`_)
 #. Provide ``Block.getInputHeight`` for determining the height of a block from blueprints. (`PR#1927 <https://github.com/terrapower/armi/pull/1927>`_)


Test patch:
diff --git a/armi/reactor/blueprints/tests/test_customIsotopics.py b/armi/reactor/blueprints/tests/test_customIsotopics.py
index e22f0a50..f20cec81 100644
--- a/armi/reactor/blueprints/tests/test_customIsotopics.py
+++ b/armi/reactor/blueprints/tests/test_customIsotopics.py
@@ -18,13 +18,17 @@ from logging import DEBUG
 
 import yamlize
 
+from armi import runLog
 from armi import settings
+from armi.physics.neutronics.settings import CONF_MCNP_LIB_BASE
+from armi.physics.neutronics.settings import CONF_NEUTRONICS_KERNEL
 from armi.physics.neutronics.settings import CONF_XS_KERNEL
 from armi.reactor import blueprints
 from armi.reactor.blueprints import isotopicOptions
 from armi.reactor.flags import Flags
-from armi import runLog
 from armi.tests import mockRunLogs
+from armi.utils.customExceptions import InputError
+from armi.utils.directoryChangers import TemporaryDirectoryChanger
 
 
 class TestCustomIsotopics(unittest.TestCase):
@@ -529,6 +533,8 @@ nuclide flags:
     SI: {burn: true, xs: true}
     MO: {burn: true, xs: true}
     W: {burn: true, xs: true}
+    ZN: {burn: true, xs: true}
+    O: {burn: true, xs: true}
 blocks:
     uzr fuel: &block_0
         fuel:
@@ -546,6 +552,14 @@ blocks:
             id: 0.0
             mult: 1.0
             od: 10.0
+        dummy:
+            shape: Circle
+            material: ZnO
+            Tinput: 25.0
+            Thot: 600.0
+            id: 0.0
+            mult: 1.0
+            od: 10.0
 assemblies:
     fuel a:
         specifier: IC
@@ -568,3 +582,47 @@ assemblies:
         self.assertNotIn("FE56", nd)  # natural isotopic not requested
         self.assertNotIn("FE51", nd)  # un-natural
         self.assertNotIn("FE", nd)
+
+    def test_eleExpandInfoBasedOnCodeENDF(self):
+        with TemporaryDirectoryChanger():
+            # Reference elements to expand by library
+            ref_E70_elem = ["C", "V", "ZN"]
+            ref_E71_elem = ["C"]
+            ref_E80_elem = []
+
+            # Load settings and set neutronics kernel to MCNP
+            cs = settings.Settings()
+            cs = cs.modified(newSettings={CONF_NEUTRONICS_KERNEL: "MCNP"})
+
+            # Set ENDF/B-VII.0 as MCNP cross section library base
+            cs = cs.modified(newSettings={CONF_MCNP_LIB_BASE: "ENDF/B-VII.0"})
+            eleToKeep, expansions = isotopicOptions.eleExpandInfoBasedOnCodeENDF(cs)
+            E70_elem = [x.label for x in eleToKeep]
+
+            # Set ENDF/B-VII.1 as MCNP cross section library base
+            cs = cs.modified(newSettings={CONF_MCNP_LIB_BASE: "ENDF/B-VII.1"})
+            eleToKeep, expansions = isotopicOptions.eleExpandInfoBasedOnCodeENDF(cs)
+            E71_elem = [x.label for x in eleToKeep]
+
+            # Set ENDF/B-VIII.0 as MCNP cross section library base
+            cs = cs.modified(newSettings={CONF_MCNP_LIB_BASE: "ENDF/B-VIII.0"})
+            eleToKeep, expansions = isotopicOptions.eleExpandInfoBasedOnCodeENDF(cs)
+            E80_elem = [x.label for x in eleToKeep]
+
+            # Assert equality of returned elements to reference elements
+            self.assertEqual(sorted(E70_elem), sorted(ref_E70_elem))
+            self.assertEqual(sorted(E71_elem), sorted(ref_E71_elem))
+            self.assertEqual(sorted(E80_elem), sorted(ref_E80_elem))
+
+            # Disallowed inputs
+            not_allowed = ["ENDF/B-VIIII.0", "ENDF/B-VI.0", "JEFF-3.3"]
+            # Assert raise InputError in case of invalid library setting
+            for x in not_allowed:
+                with self.assertRaises(InputError) as context:
+                    cs = cs.modified(newSettings={CONF_MCNP_LIB_BASE: x})
+                    _ = isotopicOptions.eleExpandInfoBasedOnCodeENDF(cs)
+
+                self.assertTrue(
+                    "Failed to determine nuclides for modeling"
+                    in str(context.exception)
+                )