Skip to content

Commit

Permalink
Fission Product Model with Explicit Fission Products for all Depletab…
Browse files Browse the repository at this point in the history
…le Components (#1067)

* Update the fission product model to fix a bug where all
nuclides were not being initialized in non-fuel depletable
components when using the `explicitFissionProducts` model.

Add testing for the updated implementation.

* Update release notes

* Bug fix

* Update interactDistributeState

* Skip nuclides with 0 ndens in reaction rate calc

* Black formatting

* Add limiter on the interactCoupled method for the LatticePhysicsInterface to only run coupled cross section generation at time node 0.

* Address reviewer comment

* Update docstring

* Add dummy nuclides for all XS types

* Fix dummy nuclides for ISOTXS

* Improve test coverage.

* More test coverage

* Black formatting

* Rename files to test file path case sensitivity on Linux

* Update expected filename to be all caps

* One last name update. Sorry, can't test this locally on Windows!

Co-authored-by: Arrielle Opotowsky <c-aopotowsky@terrapower.com>
Co-authored-by: Michael Jarrett <mjarrett@terrapower.com>
  • Loading branch information
3 people committed Jan 9, 2023
1 parent 4272611 commit faed748
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 93 deletions.
57 changes: 55 additions & 2 deletions armi/nuclearDataIO/cccc/isotxs.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,59 @@ def compareNuclideXS(nuc1, nuc2, tolerance=0.0, verbose=False):
return equal


def addDummyNuclidesToLibrary(lib, dummyNuclides):
"""
This method adds DUMMY nuclides to the current ISOTXS library.
Parameters
----------
lib : obj
ISOTXS library object
dummyNuclides: list
List of DUMMY nuclide objects that will be copied and added to the GAMISO file
Notes
-----
Since MC2-3 does not write DUMMY nuclide information for GAMISO files, this is necessary to provide a
consistent set of nuclide-level data across all the nuclides in a
:py:class:`~armi.nuclearDataIO.xsLibraries.XSLibrary`.
"""
if not dummyNuclides:
runLog.important("No dummy nuclide data provided to be added to {}".format(lib))
return False
elif len(lib.xsIDs) > 1:
runLog.warning(
"Cannot add dummy nuclide data to ISOTXS library {} containing data for more than 1 XS ID.".format(
lib
)
)
return False

dummyNuclideKeysAddedToLibrary = []
for dummyNuclide in dummyNuclides:
dummyKey = dummyNuclide.nucLabel
if len(lib.xsIDs):
dummyKey += lib.xsIDs[0]
if dummyKey in lib:
continue

newDummy = xsNuclides.XSNuclide(lib, dummyKey)
# Copy isotxs metadata from the isotxs metadata of the given dummy nuclide
for kk, vv in dummyNuclide.isotxsMetadata.items():
if kk in ["jj", "jband"]:
newDummy.isotxsMetadata[kk] = {}
for mm in vv:
newDummy.isotxsMetadata[kk][mm] = 1
else:
newDummy.isotxsMetadata[kk] = vv

lib[dummyKey] = newDummy
dummyNuclideKeysAddedToLibrary.append(dummyKey)

return any(dummyNuclideKeysAddedToLibrary)


class _IsotxsIO(cccc.Stream):
"""
A semi-abstract stream for reading and writing to a :py:class:`~armi.nuclearDataIO.isotxs.Isotxs`.
Expand Down Expand Up @@ -313,10 +366,10 @@ def _computeNuclideRecordOffset(self):
This is not used within ARMI, because it can compute it arbitrarily. Other codes use this to seek to a
specific position within an ISOTXS file.
"""
recordsPerNuclude = [
recordsPerNuclide = [
self._computeNumIsotxsRecords(nuc) for nuc in self._lib.nuclides
]
return [sum(recordsPerNuclude[0:ii]) for ii in range(len(self._lib))]
return [sum(recordsPerNuclide[0:ii]) for ii in range(len(self._lib))]

def _computeNumIsotxsRecords(self, nuclide):
"""Compute the number of ISOTXS records for a specific nuclide."""
Expand Down
2 changes: 1 addition & 1 deletion armi/nuclearDataIO/cccc/tests/test_gamiso.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

THIS_DIR = os.path.dirname(__file__)
FIXTURE_DIR = os.path.join(THIS_DIR, "..", "..", "tests", "fixtures")
GAMISO_AA = os.path.join(FIXTURE_DIR, "mc2v3-AA.gamiso")
GAMISO_AA = os.path.join(FIXTURE_DIR, "AA.GAMISO")


class TestGamiso(unittest.TestCase):
Expand Down
3 changes: 0 additions & 3 deletions armi/nuclearDataIO/tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
*isotxs*
*gamiso*
*pmatrx*
*dlayxs*
*.out
*.sum
Expand Down
Binary file added armi/nuclearDataIO/tests/fixtures/AA.GAMISO
Binary file not shown.
Binary file added armi/nuclearDataIO/tests/fixtures/AA.PMATRX
Binary file not shown.
Binary file added armi/nuclearDataIO/tests/fixtures/AB.GAMISO
Binary file not shown.
Binary file added armi/nuclearDataIO/tests/fixtures/AB.PMATRX
Binary file not shown.
Binary file added armi/nuclearDataIO/tests/fixtures/ISOAA
Binary file not shown.
Binary file added armi/nuclearDataIO/tests/fixtures/ISOAB
Binary file not shown.
67 changes: 58 additions & 9 deletions armi/nuclearDataIO/tests/test_xsLibraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@
# CCCC fixtures are less fancy than these merging ones.
FIXTURE_DIR_CCCC = os.path.join(os.path.dirname(isotxs.__file__), "tests", "fixtures")

ISOTXS_AA = os.path.join(FIXTURE_DIR, "mc2v3-AA.isotxs")
ISOTXS_AB = os.path.join(FIXTURE_DIR, "mc2v3-AB.isotxs")
ISOTXS_AA = os.path.join(FIXTURE_DIR, "ISOAA")
ISOTXS_AB = os.path.join(FIXTURE_DIR, "ISOAB")
ISOTXS_AA_AB = os.path.join(FIXTURE_DIR, "combined-AA-AB.isotxs")
ISOTXS_LUMPED = os.path.join(FIXTURE_DIR, "combined-and-lumped-AA-AB.isotxs")

PMATRX_AA = os.path.join(FIXTURE_DIR, "mc2v3-AA.pmatrx")
PMATRX_AB = os.path.join(FIXTURE_DIR, "mc2v3-AB.pmatrx")
PMATRX_AA = os.path.join(FIXTURE_DIR, "AA.PMATRX")
PMATRX_AB = os.path.join(FIXTURE_DIR, "AB.PMATRX")
PMATRX_AA_AB = os.path.join(FIXTURE_DIR, "combined-AA-AB.pmatrx")
PMATRX_LUMPED = os.path.join(FIXTURE_DIR, "combined-and-lumped-AA-AB.pmatrx")

GAMISO_AA = os.path.join(FIXTURE_DIR, "mc2v3-AA.gamiso")
GAMISO_AB = os.path.join(FIXTURE_DIR, "mc2v3-AB.gamiso")
GAMISO_AA = os.path.join(FIXTURE_DIR, "AA.GAMISO")
GAMISO_AB = os.path.join(FIXTURE_DIR, "AB.GAMISO")
GAMISO_AA_AB = os.path.join(FIXTURE_DIR, "combined-AA-AB.gamiso")
GAMISO_LUMPED = os.path.join(FIXTURE_DIR, "combined-and-lumped-AA-AB.gamiso")

Expand Down Expand Up @@ -260,7 +260,7 @@ def test_mergeFailsWithNonIsotxsFiles(self):
lib = xsLibraries.IsotxsLibrary()
xsLibraries.mergeXSLibrariesInWorkingDirectory(lib)
self.assertIn(
f"Ignoring file {dummyFileName} in the merging of ISOXX files",
f"{dummyFileName} in the merging of ISOXX files",
log.getStdoutValue(),
)
finally:
Expand Down Expand Up @@ -401,8 +401,6 @@ def assert_contains_only(self, container, shouldBeThere, shouldNotBeThere):
self.assertEqual(set(), container & set(shouldNotBeThere))


# LOOK OUT, THIS GETS DELETED LATER ON SO IT DOESN'T RUN... IT IS AN ABSTRACT CLASS!!
# LOOK OUT, THIS GETS DELETED LATER ON SO IT DOESN'T RUN... IT IS AN ABSTRACT CLASS!!
# LOOK OUT, THIS GETS DELETED LATER ON SO IT DOESN'T RUN... IT IS AN ABSTRACT CLASS!!
class TestXSlibraryMerging(unittest.TestCase, TempFileMixin):
"""A shared class that defines tests that should be true for all IsotxsLibrary merging."""
Expand Down Expand Up @@ -604,6 +602,57 @@ def getLibLumpedPath(self):
return GAMISO_LUMPED


class Combined_merge_Tests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.isotxsAA = None
cls.isotxsAB = None
cls.gamisoAA = None
cls.gamisoAB = None
cls.pmatrxAA = None
cls.pmatrxAB = None
cls.libCombined = None

@classmethod
def tearDownClass(cls):
cls.isotxsAA = None
cls.isotxsAB = None
cls.gamisoAA = None
cls.gamisoAB = None
cls.pmatrxAA = None
cls.pmatrxAB = None
cls.libCombined = None
del cls.isotxsAA
del cls.isotxsAB
del cls.gamisoAA
del cls.gamisoAB
del cls.pmatrxAA
del cls.pmatrxAB
del cls.libCombined

def setUp(self):
# load a library that is in the ARMI tree. This should
# be a small library with LFPs, Actinides, structure, and coolant
for attrName, path, readFunc in [
("isotxsAA", ISOTXS_AA, isotxs.readBinary),
("gamisoAA", GAMISO_AA, gamiso.readBinary),
("pmatrxAA", PMATRX_AA, pmatrx.readBinary),
("isotxsAB", ISOTXS_AB, isotxs.readBinary),
("gamisoAB", GAMISO_AB, gamiso.readBinary),
("pmatrxAB", PMATRX_AB, pmatrx.readBinary),
("libCombined", ISOTXS_AA_AB, isotxs.readBinary),
]:
if getattr(self.__class__, attrName) is None:
setattr(self.__class__, attrName, readFunc(path))

def test_mergeAllXSLibFiles(self):
lib = xsLibraries.IsotxsLibrary()
xsLibraries.mergeXSLibrariesInWorkingDirectory(
lib, xsLibrarySuffix="", mergeGammaLibs=True, alternateDirectory=FIXTURE_DIR
)
self.assertEqual(set(lib.nuclideLabels), set(self.libCombined.nuclideLabels))


# Remove the abstract class, so that it does not run (all tests would fail)
del TestXSlibraryMerging

Expand Down
93 changes: 70 additions & 23 deletions armi/nuclearDataIO/xsLibraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from armi import runLog
from armi.nucDirectory import nuclideBases
from armi.nuclearDataIO.nuclearFileMetadata import NuclideXSMetadata, RegionXSMetadata
from armi.nuclearDataIO import xsNuclides
from armi.utils import properties

_ISOTXS_EXT = "ISO"
Expand Down Expand Up @@ -144,10 +145,22 @@ def getISOTXSLibrariesToMerge(xsLibrarySuffix, xsLibFileNames):
return isosToMerge


def mergeXSLibrariesInWorkingDirectory(lib, xsLibrarySuffix="", mergeGammaLibs=False):
def mergeXSLibrariesInWorkingDirectory(
lib,
xsLibrarySuffix="",
mergeGammaLibs=False,
alternateDirectory=None,
):
"""
Merge neutron (ISOTXS) and gamma (GAMISO/PMATRX) library data into the provided library.
Notes
-----
Convention is for fuel XS id to come first alphabetically (A, B, C, etc.) and then be
followed by non-fuel. This should allow `referenceDummyNuclide` to be defined before
it is needed by a non-fuel cross section, but if the convention is not followed then
this could cause an issue.
Parameters
----------
lib : obj
Expand All @@ -161,18 +174,25 @@ def mergeXSLibrariesInWorkingDirectory(lib, xsLibrarySuffix="", mergeGammaLibs=F
mergeGammaLibs : bool, optional
If True, the GAMISO and PMATRX files that correspond to the ISOTXS library will be merged. Note: if these
files do not exist this will fail.
alternateDirectory : str, optional
An alternate directory in which to search for files other than the working directory. The main purpose
of this is for testing, but it could also be useful to users.
"""
# pylint: disable=import-outside-toplevel) ; avoid cyclic import with isotxs bringing this in for data structure
from armi.nuclearDataIO.cccc import isotxs
from armi.nuclearDataIO.cccc import gamiso
from armi.nuclearDataIO.cccc import pmatrx
from armi import nuclearDataIO

baseDir = alternateDirectory or os.getcwd()
globPath = os.path.join(baseDir, _ISOTXS_EXT + "*")
xsLibFiles = getISOTXSLibrariesToMerge(
xsLibrarySuffix, [iso for iso in glob.glob(_ISOTXS_EXT + "*")]
xsLibrarySuffix, [iso for iso in glob.glob(globPath)]
)
librariesToMerge = []
neutronVelocities = {} # Dictionary of neutron velocities from each ISOTXS file
referenceDummyNuclides = None
for xsLibFilePath in sorted(xsLibFiles):
try:
xsID = re.search("ISO([A-Z0-9]{2})", xsLibFilePath).group(
Expand All @@ -197,21 +217,48 @@ def mergeXSLibrariesInWorkingDirectory(lib, xsLibrarySuffix="", mergeGammaLibs=F
)
)
continue

neutronLibrary = isotxs.readBinary(xsLibFilePath)
neutronVelocities[xsID] = neutronLibrary.neutronVelocity
librariesToMerge.append(neutronLibrary)

dummyNuclidesInNeutron = [
nuc
for nuc in neutronLibrary.nuclides
if isinstance(nuc._base, nuclideBases.DummyNuclideBase)
]
if not dummyNuclidesInNeutron:
runLog.info(f"Adding dummy nuclides to library {xsID}")
addedDummyData = isotxs.addDummyNuclidesToLibrary(
neutronLibrary, referenceDummyNuclides
) # Add DUMMY nuclide data not produced by MC2-3
isotxsLibraryPath = os.path.join(
baseDir,
nuclearDataIO.getExpectedISOTXSFileName(
suffix=xsLibrarySuffix, xsID=xsID
),
)
isotxsDummyPath = isotxsLibraryPath
isotxs.writeBinary(neutronLibrary, isotxsDummyPath)
neutronLibraryDummyData = isotxs.readBinary(isotxsDummyPath)
librariesToMerge.append(neutronLibraryDummyData)
dummyNuclidesInNeutron = referenceDummyNuclides
else:
librariesToMerge.append(neutronLibrary)
if not referenceDummyNuclides:
referenceDummyNuclides = dummyNuclidesInNeutron

if mergeGammaLibs:
dummyNuclides = [
nuc
for nuc in neutronLibrary.nuclides
if isinstance(nuc._base, nuclideBases.DummyNuclideBase)
]

gamisoLibraryPath = nuclearDataIO.getExpectedGAMISOFileName(
suffix=xsLibrarySuffix, xsID=xsID
gamisoLibraryPath = os.path.join(
baseDir,
nuclearDataIO.getExpectedGAMISOFileName(
suffix=xsLibrarySuffix, xsID=xsID
),
)
pmatrxLibraryPath = nuclearDataIO.getExpectedPMATRXFileName(
suffix=xsLibrarySuffix, xsID=xsID
pmatrxLibraryPath = os.path.join(
baseDir,
nuclearDataIO.getExpectedPMATRXFileName(
suffix=xsLibrarySuffix, xsID=xsID
),
)

# Check if the gamiso and pmatrx data paths exist with the xs library suffix so that
Expand All @@ -226,18 +273,20 @@ def mergeXSLibrariesInWorkingDirectory(lib, xsLibrarySuffix="", mergeGammaLibs=F
f"Attempting to find GAMISO/PMATRX data with "
f"only XS ID {xsID} instead."
)
gamisoLibraryPath = nuclearDataIO.getExpectedGAMISOFileName(xsID=xsID)
pmatrxLibraryPath = nuclearDataIO.getExpectedPMATRXFileName(xsID=xsID)
gamisoLibraryPath = os.path.join(
baseDir, nuclearDataIO.getExpectedGAMISOFileName(xsID=xsID)
)
pmatrxLibraryPath = os.path.join(
baseDir, nuclearDataIO.getExpectedPMATRXFileName(xsID=xsID)
)

# GAMISO data
gammaLibrary = gamiso.readBinary(gamisoLibraryPath)
addedDummyData = gamiso.addDummyNuclidesToLibrary(
gammaLibrary, dummyNuclides
gammaLibrary, dummyNuclidesInNeutron
) # Add DUMMY nuclide data not produced by MC2-3
if addedDummyData:
gamisoDummyPath = os.path.abspath(
os.path.join(os.getcwd(), gamisoLibraryPath)
)
gamisoDummyPath = gamisoLibraryPath
gamiso.writeBinary(gammaLibrary, gamisoDummyPath)
gammaLibraryDummyData = gamiso.readBinary(gamisoDummyPath)
librariesToMerge.append(gammaLibraryDummyData)
Expand All @@ -247,12 +296,10 @@ def mergeXSLibrariesInWorkingDirectory(lib, xsLibrarySuffix="", mergeGammaLibs=F
# PMATRX data
pmatrxLibrary = pmatrx.readBinary(pmatrxLibraryPath)
addedDummyData = pmatrx.addDummyNuclidesToLibrary(
pmatrxLibrary, dummyNuclides
pmatrxLibrary, dummyNuclidesInNeutron
) # Add DUMMY nuclide data not produced by MC2-3
if addedDummyData:
pmatrxDummyPath = os.path.abspath(
os.path.join(os.getcwd(), pmatrxLibraryPath)
)
pmatrxDummyPath = pmatrxLibraryPath
pmatrx.writeBinary(pmatrxLibrary, pmatrxDummyPath)
pmatrxLibraryDummyData = pmatrx.readBinary(pmatrxDummyPath)
librariesToMerge.append(pmatrxLibraryDummyData)
Expand Down
Loading

0 comments on commit faed748

Please sign in to comment.