Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loading Unstable Nuclides from the RIPL-3 Database using the ARMI_RIPL_PATH Environment Variable #74

Merged
merged 2 commits into from
May 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 93 additions & 28 deletions armi/nucDirectory/nuclideBases.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
# limitations under the License.

r"""
The nuclideBases module classes for providing *base* nuclide information, such as
Z, A, state and energy release.
The nuclideBases module classes for providing *base* nuclide information, such as Z, A, state and energy release.

For details on how to setup the RIPL-3 data files to process extra nuclide data see:
:doc:`/user/user_install`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, some justification as to why we are operating by default with a pruned set of nuclides, and/or why one would want the full data set could be useful. Something to the tune of "For most reactor calculations, it is typical to reduce the dimensionality of depletion by ignoring or lumping together nuclides that aren't important. For other tasks, like detailed decay or activation analysis, a full set of nuclides may be more important."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this to the user_install.rst file and added a link to that documentation here.

The nuclide class structure is outlined in :ref:`nuclide-bases-class-diagram`.

Expand Down Expand Up @@ -61,48 +63,43 @@
<NuclideBase U235: Z:92, A:235, S:0, label:U235, mc2id:U-2355>
U235_7


Table of All nuclides:

.. exec::
from tabulate import tabulate
from armi.nucDirectory import nuclideBases

attributes = [ 'type',
'name',
'a',
'z',
'state',
'weight',
'label',
'mc2id',
'getMcc3Id']
attributes = ['name',
'a',
'z',
'state',
'weight',
'label',
'type']

def getAttributes(nuc):
return [
':py:class:`~armi.nucDirectory.nuclideBases.{}`'.format(nuc.__class__.__name__),
'``{}``'.format(nuc.name),
'``{}``'.format(nuc.a),
'``{}``'.format(nuc.z),
'``{}``'.format(nuc.state),
'``{}``'.format(nuc.weight),
'``{}``'.format(nuc.label),
'``{}``'.format(nuc.mc2id),
'``{}``'.format(nuc.getMcc3Id()),
':py:class:`~armi.nucDirectory.nuclideBases.{}`'.format(nuc.__class__.__name__),
]

sortedNucs = sorted(nuclideBases.instances, key=lambda nb: (nb.z, nb.a))

return create_table(tabulate(tabular_data=[getAttributes(nuc) for nuc in sortedNucs],
headers=attributes,
tablefmt='rst'),
caption='List of nulides')
caption='List of nuclides')

"""

import os
import pathlib
import zlib

import yaml
import collections

import armi
from armi.nucDirectory import elements
Expand All @@ -115,6 +112,7 @@ def getAttributes(nuc):
# unphysically. This is a bit of a crutch for the global state that is the nuclide
# directory.
_burnChainImposed = False
_burnChainHash = None

instances = []

Expand All @@ -129,7 +127,6 @@ def getAttributes(nuc):

byMcnpId = {}


byAAAZZZSId = {}

# lookup table from https://t2.lanl.gov/nis/data/endf/endfvii-n.html
Expand All @@ -148,6 +145,9 @@ def getAttributes(nuc):
"TC": 99,
}

_riplEnvironVariable = "ARMI_RIPL_PATH"
RIPL_PATH = None


def isotopes(z):
return elements.byZ[z].nuclideBases
Expand Down Expand Up @@ -378,15 +378,29 @@ def imposeBurnChain(burnChainStream):
armi.nucDirectory.transmutations : describes file format
"""
global _burnChainImposed # pylint: disable=global-statement
global _burnChainHash # pylint: disable=global-statement
if _burnChainImposed:
# We cannot apply more than one burn chain at a time, as this would lead to
# unphysical traits in the nuclide directory (e.g., duplicate decays and
# transmutations)
runLog.warning(
"Applying a burn chain when one has already been applied; "
"resetting the nuclide directory to it's default state first."
)
factory(True)

# Check that the hash of the burnChain is the same or
# different. If different then re-init the nuclides.
# The burn chain should really only be changing for
# special cases (e.g., unit testing). Note: after
# hashing is performed, the stream location has to
# be reset.
streamHash = zlib.crc32(burnChainStream.read().encode())
burnChainStream.seek(0)
if _burnChainHash is not None:
if streamHash != _burnChainHash:
# We cannot apply more than one burn chain at a time, as this would lead to
# unphysical traits in the nuclide directory (e.g., duplicate decays and
# transmutations)
runLog.warning(
"Applying a burn chain when one has already been applied; "
"resetting the nuclide directory to it's default state first."
)
factory(True)
_burnChainHash = streamHash

_burnChainImposed = True
burnData = yaml.load(burnChainStream, Loader=yaml.FullLoader)
for nucName, burnInfo in burnData.items():
Expand Down Expand Up @@ -440,6 +454,57 @@ def factory(force=False):
__readMc2Nuclides()
_completeNaturalNuclideBases()
elements.deriveNaturalWeights()
__readRiplDecayData()


def __readRiplDecayData():
"""
Read in the RIPL-3 decay data files and update nuclide bases.

Notes
-----
This makes an assumption that the RIPL-3 data files have a
`z???.dat` naming convention and assumes that there are 118
total data files in the package.

The processing is skipped if the ``ARMI_RIPL_PATH`` environment
variable has not been set.

Raises
------
ValueError
If the ``ARMI_RIPL_PATH`` is defined, but set incorrectly.
"""
from armi.nuclearDataIO import ripl

global RIPL_PATH

riplPath = os.environ.get(_riplEnvironVariable, None)
if riplPath is None:
return None

path = pathlib.Path(riplPath)
if not path.exists() or not path.is_dir():
raise ValueError(f"`{_riplEnvironVariable}`: {path} is invalid.")

# Check for all (.dat) data files within the directory. These
# are ordered from z000.dat to z117.dat. If all files do not
# exist then an exception is thrown for the missing data files.
numRIPLDataFiles = 118
dataFileNames = ["z{:>03d}.dat".format(i) for i in range(0, numRIPLDataFiles)]
missingFileNames = []
for df in dataFileNames:
expectedDataFilePath = os.path.abspath(os.path.join(path, df))
if not os.path.exists(expectedDataFilePath):
missingFileNames.append(df)
if missingFileNames:
raise ValueError(
f"There are {len(missingFileNames)} missing RIPL data files in `{_riplEnvironVariable}`: {path}.\n"
f"The following data files were expected: {missingFileNames}"
)

ripl.makeDecayConstantTable(directory=path)
RIPL_PATH = path


def _completeNaturalNuclideBases():
Expand Down
6 changes: 1 addition & 5 deletions armi/nuclearDataIO/ripl.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from armi.settings.caseSettings import Settings

DECAY_CONSTANTS = {}
MINIMUM_HALFLIFE = 1.0e-06
MINIMUM_HALFLIFE = 2.8e-7
STABLE_FLAG = -1
UNKNOWN_HALFLIFE = -2
EXIT_DATA_FILE = -3
Expand Down Expand Up @@ -96,10 +96,6 @@ def getNuclideDecayConstants(fileName):
m += 1

else:
msg = "metastable state for {}m{} greater than 1 -- skipping".format(
symb, m
)
runLog.warning(msg)
level += numLevels + 1

elif halflife < MINIMUM_HALFLIFE or halflife == UNKNOWN_HALFLIFE:
Expand Down
18 changes: 18 additions & 0 deletions doc/user/user_install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,21 @@ If it worked, you should see the (classic) ARMI splash screen and no errors::


If it works, congrats! So far so good.

Optional Setup
--------------
This subsection provides setup information for optional external data packages.

RIPL-3 Nuclide Decay Database
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The RIPL-3 decay files (``levels.zip``) can be downloaded from `<https://www-nds.iaea.org/RIPL-3/levels/>`_.

By default, nuclides within :py:mod:`armi.nucDirectory.nuclideBases` are initialized from
a subset of the RIPL-3 database, which ships with ARMI. The base data set contains 2339
nuclides and RIPL-3 decay data set increases this to 4379 nuclides. The RIPL-3 decay data
files mainly add metastable nuclides and other exotic nuclides that could be important for
detailed depletion/decay models or activation analyses.

Once the ``levels.zip`` file is downloaded and unzipped, an environment variable :envvar:`ARMI_RIPL_PATH`
should be created and set to the directory containing the ``z*.dat`` files.