diff --git a/armi/bookkeeping/db/tests/test_database3.py b/armi/bookkeeping/db/tests/test_database3.py index c4e769ea9..91246ac57 100644 --- a/armi/bookkeeping/db/tests/test_database3.py +++ b/armi/bookkeeping/db/tests/test_database3.py @@ -29,8 +29,8 @@ from armi.reactor.excoreStructure import ExcoreCollection, ExcoreStructure from armi.reactor.reactors import Core, Reactor from armi.reactor.spentFuelPool import SpentFuelPool -from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings from armi.settings.fwSettings.globalSettings import CONF_SORT_REACTOR +from armi.testing import loadTestReactor, reduceTestReactorRings from armi.tests import TEST_ROOT, mockRunLogs from armi.utils import getPreviousTimeNode, safeCopy from armi.utils.directoryChangers import TemporaryDirectoryChanger diff --git a/armi/bookkeeping/db/tests/test_databaseInterface.py b/armi/bookkeeping/db/tests/test_databaseInterface.py index e5d066553..a4c12ded4 100644 --- a/armi/bookkeeping/db/tests/test_databaseInterface.py +++ b/armi/bookkeeping/db/tests/test_databaseInterface.py @@ -29,7 +29,7 @@ from armi.physics.neutronics.settings import CONF_LOADING_FILE from armi.reactor import grids from armi.reactor.flags import Flags -from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings +from armi.testing import loadTestReactor, reduceTestReactorRings from armi.tests import TEST_ROOT from armi.utils import directoryChangers diff --git a/armi/bookkeeping/report/tests/test_report.py b/armi/bookkeeping/report/tests/test_report.py index 24e0ac9b8..b395781d6 100644 --- a/armi/bookkeeping/report/tests/test_report.py +++ b/armi/bookkeeping/report/tests/test_report.py @@ -40,7 +40,7 @@ writeCycleSummary, writeWelcomeHeaders, ) -from armi.reactor.tests.test_reactors import loadTestReactor +from armi.testing import loadTestReactor from armi.tests import mockRunLogs from armi.utils.directoryChangers import TemporaryDirectoryChanger diff --git a/armi/cli/tests/test_runEntryPoint.py b/armi/cli/tests/test_runEntryPoint.py index 97fcacb3e..2094f6222 100644 --- a/armi/cli/tests/test_runEntryPoint.py +++ b/armi/cli/tests/test_runEntryPoint.py @@ -34,7 +34,7 @@ from armi.cli.run import RunEntryPoint from armi.cli.runSuite import RunSuiteCommand from armi.physics.neutronics.diffIsotxs import CompareIsotxsLibraries -from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings +from armi.testing import loadTestReactor, reduceTestReactorRings from armi.tests import ARMI_RUN_PATH, TEST_ROOT, mockRunLogs from armi.utils.directoryChangers import TemporaryDirectoryChanger from armi.utils.dynamicImporter import getEntireFamilyTree diff --git a/armi/context.py b/armi/context.py index 76b9e5c02..8436ceae5 100644 --- a/armi/context.py +++ b/armi/context.py @@ -97,12 +97,7 @@ def setMode(cls, mode): try: - # Check for MPI. The mpi4py module uses cPickle to serialize python objects in preparation for - # network transmission. Sometimes, when cPickle fails, it gives very cryptic error messages that - # do not help much. If you uncomment th following line, you can trick mpi4py into using the - # pure-python pickle module in place of cPickle and now you will generally get much more - # meaningful and useful error messages Then comment it back out because it's slow. - # import sys, pickle; sys.modules['cPickle'] = pickle + # Check for MPI from mpi4py import MPI MPI_COMM = MPI.COMM_WORLD diff --git a/armi/physics/neutronics/latticePhysics/tests/test_latticeWriter.py b/armi/physics/neutronics/latticePhysics/tests/test_latticeWriter.py index 90c96accf..89f1b1225 100644 --- a/armi/physics/neutronics/latticePhysics/tests/test_latticeWriter.py +++ b/armi/physics/neutronics/latticePhysics/tests/test_latticeWriter.py @@ -30,7 +30,7 @@ CONF_DISABLE_BLOCK_TYPE_EXCLUSION_IN_XS_GENERATION, CONF_XS_BLOCK_REPRESENTATION, ) -from armi.reactor.tests.test_reactors import loadTestReactor +from armi.testing import loadTestReactor from armi.tests import TEST_ROOT diff --git a/armi/physics/neutronics/tests/test_crossSectionTable.py b/armi/physics/neutronics/tests/test_crossSectionTable.py index c0f23f7d0..a961be50e 100644 --- a/armi/physics/neutronics/tests/test_crossSectionTable.py +++ b/armi/physics/neutronics/tests/test_crossSectionTable.py @@ -23,8 +23,8 @@ ) from armi.physics.neutronics.latticePhysics import ORDER from armi.reactor.tests.test_blocks import loadTestBlock -from armi.reactor.tests.test_reactors import loadTestReactor from armi.settings import Settings +from armi.testing import loadTestReactor from armi.tests import ISOAA_PATH diff --git a/armi/physics/neutronics/tests/test_macroXSGenerationInterface.py b/armi/physics/neutronics/tests/test_macroXSGenerationInterface.py index 6131c17a8..2b202fdcd 100644 --- a/armi/physics/neutronics/tests/test_macroXSGenerationInterface.py +++ b/armi/physics/neutronics/tests/test_macroXSGenerationInterface.py @@ -20,8 +20,8 @@ from armi.physics.neutronics.macroXSGenerationInterface import ( MacroXSGenerationInterface, ) -from armi.reactor.tests.test_reactors import loadTestReactor from armi.settings import Settings +from armi.testing import loadTestReactor from armi.tests import ISOAA_PATH diff --git a/armi/reactor/converters/parameterSweeps/tests/test_paramSweepConverters.py b/armi/reactor/converters/parameterSweeps/tests/test_paramSweepConverters.py index 81f87fa96..af7d0dd6f 100644 --- a/armi/reactor/converters/parameterSweeps/tests/test_paramSweepConverters.py +++ b/armi/reactor/converters/parameterSweeps/tests/test_paramSweepConverters.py @@ -22,7 +22,7 @@ ParameterSweepConverter, SettingsModifier, ) -from armi.reactor.tests.test_reactors import loadTestReactor +from armi.testing import loadTestReactor from armi.tests import TEST_ROOT THIS_DIR = os.path.dirname(__file__) diff --git a/armi/reactor/converters/tests/test_axialExpansionChanger.py b/armi/reactor/converters/tests/test_axialExpansionChanger.py index a8a4708b9..49bb32bb4 100644 --- a/armi/reactor/converters/tests/test_axialExpansionChanger.py +++ b/armi/reactor/converters/tests/test_axialExpansionChanger.py @@ -40,7 +40,7 @@ areAxiallyLinked, ) from armi.reactor.flags import Flags -from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings +from armi.testing import loadTestReactor, reduceTestReactorRings from armi.tests import TEST_ROOT from armi.utils import units diff --git a/armi/reactor/converters/tests/test_blockConverter.py b/armi/reactor/converters/tests/test_blockConverter.py index e32056345..702b8eb38 100644 --- a/armi/reactor/converters/tests/test_blockConverter.py +++ b/armi/reactor/converters/tests/test_blockConverter.py @@ -26,7 +26,7 @@ from armi.reactor.converters import blockConverters from armi.reactor.flags import Flags from armi.reactor.tests.test_blocks import loadTestBlock -from armi.reactor.tests.test_reactors import TEST_ROOT, loadTestReactor +from armi.testing import TEST_ROOT, loadTestReactor from armi.utils import hexagon from armi.utils.directoryChangers import TemporaryDirectoryChanger diff --git a/armi/reactor/converters/tests/test_geometryConverters.py b/armi/reactor/converters/tests/test_geometryConverters.py index 0461e5979..5746a18bb 100644 --- a/armi/reactor/converters/tests/test_geometryConverters.py +++ b/armi/reactor/converters/tests/test_geometryConverters.py @@ -23,7 +23,7 @@ from armi.reactor import blocks, geometry, grids from armi.reactor.converters import geometryConverters, uniformMesh from armi.reactor.flags import Flags -from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings +from armi.testing import loadTestReactor, reduceTestReactorRings from armi.tests import TEST_ROOT, mockRunLogs from armi.utils import directoryChangers diff --git a/armi/reactor/converters/tests/test_meshConverters.py b/armi/reactor/converters/tests/test_meshConverters.py index 348e5d270..14f102139 100644 --- a/armi/reactor/converters/tests/test_meshConverters.py +++ b/armi/reactor/converters/tests/test_meshConverters.py @@ -17,7 +17,7 @@ import unittest from armi.reactor.converters import geometryConverters, meshConverters -from armi.reactor.tests.test_reactors import loadTestReactor +from armi.testing import loadTestReactor from armi.tests import TEST_ROOT diff --git a/armi/reactor/converters/tests/test_uniformMesh.py b/armi/reactor/converters/tests/test_uniformMesh.py index 063883e6a..d5d386f56 100644 --- a/armi/reactor/converters/tests/test_uniformMesh.py +++ b/armi/reactor/converters/tests/test_uniformMesh.py @@ -23,8 +23,8 @@ from armi.reactor.converters import uniformMesh from armi.reactor.flags import Flags from armi.reactor.tests import test_assemblies, test_blocks -from armi.reactor.tests.test_reactors import loadTestReactor, reduceTestReactorRings from armi.settings.fwSettings.globalSettings import CONF_UNIFORM_MESH_MINIMUM_SIZE +from armi.testing import loadTestReactor, reduceTestReactorRings from armi.tests import ISOAA_PATH, TEST_ROOT diff --git a/armi/reactor/tests/test_blocks.py b/armi/reactor/tests/test_blocks.py index 80ff304ec..aa6c914d4 100644 --- a/armi/reactor/tests/test_blocks.py +++ b/armi/reactor/tests/test_blocks.py @@ -37,8 +37,8 @@ from armi.reactor.components import basicShapes, complexShapes from armi.reactor.flags import Flags from armi.reactor.tests.test_assemblies import makeTestAssembly -from armi.reactor.tests.test_reactors import TEST_ROOT, loadTestReactor -from armi.tests import ISOAA_PATH +from armi.testing import loadTestReactor +from armi.tests import ISOAA_PATH, TEST_ROOT from armi.utils import densityTools, hexagon, units from armi.utils.directoryChangers import TemporaryDirectoryChanger from armi.utils.units import ( diff --git a/armi/reactor/tests/test_components.py b/armi/reactor/tests/test_components.py index eed58904c..ad32245e4 100644 --- a/armi/reactor/tests/test_components.py +++ b/armi/reactor/tests/test_components.py @@ -48,7 +48,7 @@ UnshapedVolumetricComponent, materials, ) -from armi.reactor.tests.test_reactors import loadTestReactor +from armi.testing import loadTestReactor class TestComponentFactory(unittest.TestCase): diff --git a/armi/reactor/tests/test_composites.py b/armi/reactor/tests/test_composites.py index b9cfd9093..861faa80c 100644 --- a/armi/reactor/tests/test_composites.py +++ b/armi/reactor/tests/test_composites.py @@ -28,7 +28,7 @@ from armi.reactor.composites import getReactionRateDict 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.testing import loadTestReactor from armi.tests import ISOAA_PATH, mockRunLogs diff --git a/armi/reactor/tests/test_parameters.py b/armi/reactor/tests/test_parameters.py index 38631b904..9f1a5e018 100644 --- a/armi/reactor/tests/test_parameters.py +++ b/armi/reactor/tests/test_parameters.py @@ -20,7 +20,7 @@ from armi.reactor import parameters from armi.reactor.reactorParameters import makeParametersReadOnly -from armi.reactor.tests.test_reactors import loadTestReactor +from armi.testing import loadTestReactor from armi.tests import TEST_ROOT from armi.utils.directoryChangers import TemporaryDirectoryChanger diff --git a/armi/reactor/tests/test_reactors.py b/armi/reactor/tests/test_reactors.py index 70ddf96ca..561c55247 100644 --- a/armi/reactor/tests/test_reactors.py +++ b/armi/reactor/tests/test_reactors.py @@ -36,11 +36,11 @@ CONF_ASSEM_FLAGS_SKIP_AXIAL_EXP, CONF_SORT_REACTOR, ) -from armi.tests import ARMI_RUN_PATH, TEST_ROOT, mockRunLogs +from armi.testing import loadTestReactor, reduceTestReactorRings # noqa: F401 +from armi.tests import TEST_ROOT, mockRunLogs from armi.utils import directoryChangers -THIS_DIR = os.path.dirname(__file__) -TEST_REACTOR = None # pickled string of test reactor (for fast caching) +_THIS_DIR = os.path.dirname(__file__) def buildOperatorOfEmptyHexBlocks(customSettings=None): @@ -125,95 +125,6 @@ def buildOperatorOfEmptyCartesianBlocks(customSettings=None): return o -""" -NOTE: If this reactor had 3 rings instead of 9, most unit tests that use it -go 4 times faster (based on testing). The problem is it would breat a LOT -of downstream tests that import this method. Probably still worth it though. -""" - - -def loadTestReactor( - inputFilePath=TEST_ROOT, - customSettings=None, - inputFileName="armiRun.yaml", -): - """ - Loads a test reactor. Can be used in other test modules. - - Parameters - ---------- - inputFilePath : str, default=TEST_ROOT - Path to the directory of the input file. - - customSettings : dict with str keys and values of any type, default=None - For each key in customSettings, the cs which is loaded from the - armiRun.yaml will be overwritten to the value given in customSettings - for that key. - - inputFileName : str, default="armiRun.yaml" - Name of the input file to run. - - Returns - ------- - o : Operator - r : Reactor - """ - global TEST_REACTOR - fName = os.path.join(inputFilePath, inputFileName) - customSettings = customSettings or {} - isPickeledReactor = fName == ARMI_RUN_PATH and customSettings == {} - - if isPickeledReactor and TEST_REACTOR: - # return test reactor only if no custom settings are needed. - o, r, assemNum = pickle.loads(TEST_REACTOR) - o.reattach(r, o.cs) - return o, r - - cs = settings.Settings(fName=fName) - - # Overwrite settings if desired - if customSettings: - cs = cs.modified(newSettings=customSettings) - - if "verbosity" not in customSettings: - runLog.setVerbosity("error") - - newSettings = {} - cs = cs.modified(newSettings=newSettings) - - o = operators.factory(cs) - r = reactors.loadFromCs(cs) - - o.initializeInterfaces(r) - - o.r.core.regenAssemblyLists() - - if isPickeledReactor: - # cache it for fast load for other future tests - # protocol=2 allows for classes with __slots__ but not __getstate__ to be pickled - TEST_REACTOR = pickle.dumps((o, o.r, o.r.p.maxAssemNum), protocol=2) - - return o, o.r - - -def reduceTestReactorRings(r, cs, maxNumRings): - """Helper method for the test reactor above. - - The goal is to reduce the size of the reactor for tests that don't neeed - such a large reactor, and would run much faster with a smaller one. - """ - maxRings = r.core.getNumRings() - if maxNumRings > maxRings: - runLog.info("The test reactor has a maximum of {} rings.".format(maxRings)) - return - elif maxNumRings <= 1: - raise ValueError("The test reactor must have multiple rings.") - - # reducing the size of the test reactor, by removing the outer rings - for ring in range(maxRings, maxNumRings, -1): - r.core.removeAssembliesInRing(ring, cs) - - class ReactorTests(unittest.TestCase): @classmethod def setUpClass(cls): @@ -862,7 +773,8 @@ class MockLib: for b in self.r.core.getBlocks(): b.p.mgFlux = range(5) b.p.adjMgFlux = range(5) - with directoryChangers.TemporaryDirectoryChanger(root=THIS_DIR): + + with directoryChangers.TemporaryDirectoryChanger(root=_THIS_DIR): self.r.core.saveAllFlux() def test_getFluxVector(self): diff --git a/armi/testing/__init__.py b/armi/testing/__init__.py new file mode 100644 index 000000000..994db60d9 --- /dev/null +++ b/armi/testing/__init__.py @@ -0,0 +1,117 @@ +# Copyright 2024 TerraPower, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Importable testing utilities. + +This is a very limited set of ARMI testing tools, meant to be importable as part of the ARMI API. +The goal is to provide a small set of high quality tools to help downstream ARMI developers write +tests. + +Notes +----- +This will not be a catch-all for random unit test functions. Be very sparing here. +""" +import os +import pickle + +from armi import operators, runLog, settings +from armi.reactor import reactors +from armi.tests import ARMI_RUN_PATH, TEST_ROOT + +_THIS_DIR = os.path.dirname(__file__) +_TEST_REACTOR = None # pickled string of test reactor (for fast caching) + + +def loadTestReactor( + inputFilePath=TEST_ROOT, + customSettings=None, + inputFileName="armiRun.yaml", +): + """ + Loads a test reactor. Can be used in other test modules. + + Parameters + ---------- + inputFilePath : str, default=TEST_ROOT + Path to the directory of the input file. + + customSettings : dict with str keys and values of any type, default=None + For each key in customSettings, the cs which is loaded from the armiRun.yaml will be + overwritten to the value given in customSettings for that key. + + inputFileName : str, default="armiRun.yaml" + Name of the input file to run. + + Notes + ----- + If the armiRun.yaml test reactor 3 rings instead of 9, most unit tests that use it go 4 times + faster (based on testing). The problem is it would breat a LOT of downstream tests that import + this method. It is still worth it though. + + Returns + ------- + o : Operator + r : Reactor + """ + global _TEST_REACTOR + fName = os.path.join(inputFilePath, inputFileName) + customSettings = customSettings or {} + isPickeledReactor = fName == ARMI_RUN_PATH and customSettings == {} + + if isPickeledReactor and _TEST_REACTOR: + # return test reactor only if no custom settings are needed. + o, r, assemNum = pickle.loads(_TEST_REACTOR) + o.reattach(r, o.cs) + return o, r + + cs = settings.Settings(fName=fName) + + # Overwrite settings if desired + if customSettings: + cs = cs.modified(newSettings=customSettings) + + if "verbosity" not in customSettings: + runLog.setVerbosity("error") + + cs = cs.modified(newSettings={}) + o = operators.factory(cs) + r = reactors.loadFromCs(cs) + + o.initializeInterfaces(r) + o.r.core.regenAssemblyLists() + + if isPickeledReactor: + # cache it for fast load for other future tests protocol=2 allows for classes with __slots__ + # but not __getstate__ to be pickled + _TEST_REACTOR = pickle.dumps((o, o.r, o.r.p.maxAssemNum), protocol=2) + + return o, o.r + + +def reduceTestReactorRings(r, cs, maxNumRings): + """Helper method for the test reactor above. + + The goal is to reduce the size of the reactor for tests that don't neeed such a large reactor, + and would run much faster with a smaller one. + """ + maxRings = r.core.getNumRings() + if maxNumRings > maxRings: + runLog.info("The test reactor has a maximum of {} rings.".format(maxRings)) + return + elif maxNumRings <= 1: + raise ValueError("The test reactor must have multiple rings.") + + # reducing the size of the test reactor, by removing the outer rings + for ring in range(maxRings, maxNumRings, -1): + r.core.removeAssembliesInRing(ring, cs) diff --git a/armi/tests/test_plugins.py b/armi/tests/test_plugins.py index a8f6a6b68..7852ce653 100644 --- a/armi/tests/test_plugins.py +++ b/armi/tests/test_plugins.py @@ -33,7 +33,8 @@ from armi.reactor.blocks import Block from armi.reactor.converters.axialExpansionChanger import AxialExpansionChanger from armi.reactor.flags import Flags -from armi.reactor.tests.test_reactors import TEST_ROOT, loadTestReactor +from armi.testing import loadTestReactor +from armi.tests import TEST_ROOT class PluginFlags1(plugins.ArmiPlugin): diff --git a/armi/utils/tests/test_utils.py b/armi/utils/tests/test_utils.py index c7982dfd5..b514b5692 100644 --- a/armi/utils/tests/test_utils.py +++ b/armi/utils/tests/test_utils.py @@ -20,8 +20,8 @@ import numpy as np from armi import utils -from armi.reactor.tests.test_reactors import loadTestReactor from armi.settings.caseSettings import Settings +from armi.testing import loadTestReactor from armi.tests import mockRunLogs from armi.utils import ( codeTiming, diff --git a/doc/release/0.5.rst b/doc/release/0.5.rst index f730979d6..0e49b8e5a 100644 --- a/doc/release/0.5.rst +++ b/doc/release/0.5.rst @@ -4,11 +4,13 @@ ARMI v0.5 Release Notes ARMI v0.5.1 =========== -Release Date: TBD +Release Date: TBD (March-ish, 2025) New Features ------------ #. Move instead of copy files from TemporaryDirectoryChanger. (`PR#2022 `_) +#. Creating the ``armi.testing`` module, to share ARMI testing tools. (`PR#2028 `_) +#. TBD API Changes -----------