diff --git a/armi/plugins.py b/armi/plugins.py index f772ef43c..59b953000 100644 --- a/armi/plugins.py +++ b/armi/plugins.py @@ -267,6 +267,21 @@ def onProcessCoreLoading(core, cs, dbLoad) -> None: constructing a Core from Blueprints, or after loading it from a database. """ + @staticmethod + @HOOKSPEC + def beforeReactorConstruction(cs) -> None: + """ + Function to call before the reactor is constructed. + + .. impl:: Plugins can inject code before reactor initialization. + :id: I_ARMI_PLUGIN_BEFORE_REACTOR_HOOK + :implements: R_ARMI_PLUGIN_BEFORE_REACTOR_HOOK + + This method allows for plugin developers to implement code after settings + are loaded but before the reactor is constructed. This hook is called + in :py:func:`armi.reactor.reactors.factory`. + """ + @staticmethod @HOOKSPEC def defineFlags() -> Dict[str, Union[int, flags.auto]]: diff --git a/armi/reactor/__init__.py b/armi/reactor/__init__.py index 2cd096767..c7c3981d0 100644 --- a/armi/reactor/__init__.py +++ b/armi/reactor/__init__.py @@ -51,6 +51,7 @@ from typing import Dict, Callable, Union, TYPE_CHECKING +from armi import materials from armi import plugins if TYPE_CHECKING: @@ -62,6 +63,19 @@ class ReactorPlugin(plugins.ArmiPlugin): """Plugin exposing built-in reactor components, blocks, assemblies, etc.""" + @staticmethod + @plugins.HOOKIMPL + def beforeReactorConstruction(cs) -> None: + """Just before reactor construction, update the material "registry" with user settings, + if it is set. Often it is set by the application. + """ + from armi.settings.fwSettings.globalSettings import ( + CONF_MATERIAL_NAMESPACE_ORDER, + ) + + if cs[CONF_MATERIAL_NAMESPACE_ORDER]: + materials.setMaterialNamespaceOrder(cs[CONF_MATERIAL_NAMESPACE_ORDER]) + @staticmethod @plugins.HOOKIMPL def defineBlockTypes(): diff --git a/armi/reactor/reactors.py b/armi/reactor/reactors.py index 31eae2bc4..1c5b862d5 100644 --- a/armi/reactor/reactors.py +++ b/armi/reactor/reactors.py @@ -19,7 +19,7 @@ from typing import Optional import copy -from armi import materials +from armi import getPluginManagerOrFail from armi import runLog from armi.reactor import composites from armi.reactor import reactorParameters @@ -29,7 +29,6 @@ from armi.reactor.systemLayoutInput import SystemLayoutInput from armi.settings.fwSettings.globalSettings import ( CONF_GEOM_FILE, - CONF_MATERIAL_NAMESPACE_ORDER, CONF_SORT_REACTOR, ) from armi.utils import directoryChangers @@ -180,10 +179,8 @@ def factory(cs, bp, geom: Optional[SystemLayoutInput] = None) -> Reactor: from armi.reactor import blueprints runLog.header("=========== Constructing Reactor and Verifying Inputs ===========") - # just before reactor construction, update the material "registry" with user settings, - # if it is set. Often it is set by the application. - if cs[CONF_MATERIAL_NAMESPACE_ORDER]: - materials.setMaterialNamespaceOrder(cs[CONF_MATERIAL_NAMESPACE_ORDER]) + getPluginManagerOrFail().hook.beforeReactorConstruction(cs=cs) + r = Reactor(cs.caseTitle, bp) if cs[CONF_GEOM_FILE]: diff --git a/armi/tests/test_plugins.py b/armi/tests/test_plugins.py index 209fe7336..b56aa4023 100644 --- a/armi/tests/test_plugins.py +++ b/armi/tests/test_plugins.py @@ -56,6 +56,15 @@ def getAxialExpansionChanger() -> type[SillyAxialExpansionChanger]: return SillyAxialExpansionChanger +class BeforeReactorPlugin(plugins.ArmiPlugin): + """Trivial plugin that implements the before reactor construction hook.""" + + @staticmethod + @plugins.HOOKIMPL + def beforeReactorConstruction(cs) -> None: + cs.beforeReactorConstructionFlag = True + + class TestPluginRegistration(unittest.TestCase): def setUp(self): """ @@ -119,6 +128,20 @@ def test_axialExpansionHook(self): # that plugin's axial expander self.assertIs(second, SillyAxialExpansionChanger) + def test_beforeReactorConstructionHook(self): + """Test that plugin hook successfully injects code before reactor initialization. + + .. test:: Capture code in the beforeReactorConstruction hook from reactor construction being carried out. + :id: T_ARMI_PLUGIN_BEFORE_REACTOR_HOOK + :tests: R_ARMI_PLUGIN_BEFORE_REACTOR_HOOK + """ + pm = getPluginManagerOrFail() + pm.register(BeforeReactorPlugin) + o = loadTestReactor( + TEST_ROOT, inputFileName="smallestTestReactor/armiRunSmallest.yaml" + )[0] + self.assertTrue(o.cs.beforeReactorConstructionFlag) + class TestPluginBasics(unittest.TestCase): def test_defineParameters(self): diff --git a/doc/release/0.4.rst b/doc/release/0.4.rst index 48caacdfe..c5d523384 100644 --- a/doc/release/0.4.rst +++ b/doc/release/0.4.rst @@ -14,11 +14,13 @@ New Features #. Adding ``--skip-inspection`` flag to ``CompareCases`` CLI. (`PR#1842 `_) #. Allow merging a component with zero area into another component (`PR#1858 `_) #. Use ``Block.getNumPins()`` in ``HexBlock._rotatePins()``. (`PR#1859 `_) -#. Provide utilities for determining location of a rotated object in a hexagonal lattice (``getIndexOfRotatedCell``). (`PR#1846 `_) #. Allow merging a component with zero area into another component. (`PR#1858 `_) #. Provide ``Parameter.hasCategory`` for quickly checking if a parameter is defined with a given category. (`PR#1899 `_) #. Provide ``ParameterCollection.where`` for efficient iteration over parameters who's definition matches a given condition. (`PR#1899 `_) #. Plugins can provide the ``getAxialExpansionChanger`` hook to customize axial expansion. (`PR#1870 `_) +#. New plugin hook ``beforeReactorConstruction`` added to enable plugins to process case settings before reactor init. (`PR#1945 `_) #. Provide ``Block.getInputHeight`` for determining the height of a block from blueprints. (`PR#1927 `_) #. Removing ``Assembly.doubleResolution()``. (`PR#1951 `_) #. Removing ``assemblyLists.py`` and the ``AssemblyList`` class. (`PR#1891 `_) -#. Removing ``Assembly.rotatePins`` and ``Block.rotatePins``. Prefer ``Assembly.rotate`` and ``Block.rotate``. (`PR#1846 `_) #. Transposing ``pinMgFluxes`` parameters so that leading dimension is pin index (`PR#1937 `) #. Removing ``globalFluxInterface.DoseResultsMapper`` class (`PR#1952 `) #. TBD