Skip to content
Open
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
15 changes: 15 additions & 0 deletions armi/physics/neutronics/isotopicDepletion/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2026 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.

"""This package houses helper tools that allow ARMI to communicate with external isotopic depletion programs."""
97 changes: 44 additions & 53 deletions armi/physics/neutronics/isotopicDepletion/crossSectionTable.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
"""
Module containing the CrossSectionTable class.

The CrossSectionTable is useful for performing isotopic depletion analysis by storing one-group
cross sections of interest to such an analysis. This used to live alongside the
isotopicDepletionInterface, but that proved to be an unpleasant coupling between the ARMI composite
model and the physics code contained therein. Separating it out at least means that the composite
model doesn't need to import the isotopicDepletionInterface to function.
The CrossSectionTable is useful for performing isotopic depletion analysis by storing one-group cross sections of
interest to such an analysis. This used to live alongside the isotopicDepletionInterface, but that proved to be an
unpleasant coupling between the ARMI composite model and the physics code contained therein. Separating it out at least
means that the composite model doesn't need to import the isotopicDepletionInterface to function.
"""

import collections
Expand All @@ -36,9 +35,8 @@ class CrossSectionTable(collections.OrderedDict):

It can also double as a reaction rate table.

XStable is indexed by nucNames
(nG), (nF), (n2n), (nA), (nP) and (n3n) are expected
the cross sections are returned in barns
XStable is indexed by nucNames (nG), (nF), (n2n), (nA), (nP) and (n3n) are expected the cross sections are returned
in barns.
"""

rateTypes = ("nG", "nF", "n2n", "nA", "nP", "n3n")
Expand All @@ -59,19 +57,19 @@ def add(self, nucName, nG=0.0, nF=0.0, n2n=0.0, nA=0.0, nP=0.0, n3n=0.0):

Parameters
----------
nucName - str
nucName : str
nuclide name -- e.g. 'U235'
nG - float
nG : float
(n,gamma) cross section in barns
nF - float
nF : float
(n,fission) cross section in barns
n2n - float
n2n : float
(n,2n) cross section in barns
nA - float
nA : float
(n,alpha) cross section in barns
nP - float
nP : float
(n,proton) cross section in barns
n3n - float
n3n : float
(n,3n) cross section in barns
"""
xsData = {rateType: xs for rateType, xs in zip(self.rateTypes, [nG, nF, n2n, nA, nP, n3n])}
Expand All @@ -85,13 +83,13 @@ def addMultiGroupXS(self, nucName, microMultiGroupXS, mgFlux, totalFlux=None):

Parameters
----------
nucName - str
nucName: str
nuclide name -- e.g. 'U235'
microMultiGroupXS - XSCollection
microMultiGroupXS: XSCollection
micro cross sections, typically a XSCollection from an ISOTXS
mgFlux - list like
mgFlux: list like
The flux in each energy group
totalFlux - float
totalFlux: float
The total flux. Optional argument for increased speed if already available.
"""
totalFlux = totalFlux if totalFlux is not None else sum(mgFlux)
Expand Down Expand Up @@ -127,25 +125,21 @@ def getXsecTable(
:id: I_ARMI_DEPL_TABLES1
:implements: R_ARMI_DEPL_TABLES

Loops over the reaction rates stored as ``self`` to produce a string with the cross
sections for each nuclide in the block. Cross sections may be populated by
:py:meth:`~armi.physics.neutronics.isotopicDepletion.crossSectionTable.makeReactionRateTable`
Loops over the reaction rates stored as ``self`` to produce a string with the cross sections for each
nuclide in the block. Cross sections may be populated by ``makeReactionRateTable``.

The string will have a header with the table's name formatted according to
``headerFormat`` followed by rows for each unique nuclide/reaction combination, where
each line is formatted according to ``tableFormat``.
The string will have a header with the table's name formatted according to ``headerFormat`` followed by rows
for each unique nuclide/reaction combination, where each line is formatted according to ``tableFormat``.

Parameters
----------
headerFormat: string (optional)
This is the format in which the elements of the header with be returned -- i.e. if you
use a .format() call with the case name you'll return a formatted list of strings.

This is the format in which the elements of the header with be returned -- i.e. if you use a .format() call
with the case name you'll return a formatted list of strings.
tableFormat: string (optional)
This is the format in which the elements of the table with be returned -- i.e. if you
use a .format() call with mcnpId, nG, nF, n2n, n3n, nA, and nP you'll get the format you
want. If you use a .format() call with the case name you'll return a formatted list of
string elements
This is the format in which the elements of the table with be returned -- i.e. if you use a .format() call
with mcnpId, nG, nF, n2n, n3n, nA, and nP you'll get the format you want. If you use a .format() call with
the case name you'll return a formatted list of string elements

Results
-------
Expand All @@ -159,6 +153,7 @@ def getXsecTable(
if any(dataToWrite[rateType] for rateType in self.rateTypes):
dataToWrite["mcnpId"] = mcnpNucName
output.append(tableFormat.format(**dataToWrite))

return output


Expand All @@ -168,31 +163,27 @@ def makeReactionRateTable(obj, nuclides: List = None):

Often useful in support of depletion.

.. impl:: Generate a reaction rate table with entries for (nG), (nF), (n2n), (nA), and (nP)
reactions.
.. impl:: Generate a reaction rate table with entries for (nG), (nF), (n2n), (nA), and (nP) reactions.
:id: I_ARMI_DEPL_TABLES0
:implements: R_ARMI_DEPL_TABLES

For a given composite object ``obj`` and a list of nuclides ``nuclides`` in that object,
call ``obj.getReactionRates()`` for each nuclide with a ``nDensity`` parameter of 1.0. If
``nuclides`` is not specified, use a list of all nuclides in ``obj``. This will reach
upwards through the parents of ``obj`` to the associated
:py:class:`~armi.reactor.reactors.Core` object and pull the ISOTXS library that is stored
there. If ``obj`` does not belong to a ``Core``, a warning is printed.
For a given composite object ``obj`` and a list of nuclides ``nuclides`` in that object, call
``obj.getReactionRates()`` for each nuclide with a ``nDensity`` parameter of 1.0. If ``nuclides`` is not
specified, use a list of all nuclides in ``obj``. This will reach upwards through the parents of ``obj`` to the
associated :py:class:`~armi.reactor.reactors.Core` object and pull the ISOTXS library that is stored there. If
``obj`` does not belong to a ``Core``, a warning is printed.

For each child of ``obj``, use the ISOTXS library and the cross-section ID for the
associated block to produce a reaction rate dictionary in units of inverse seconds for the
nuclide specified in the original call to ``obj.getReactionRates()``. Because ``nDensity``
was originally specified as 1.0, this dictionary actually represents the reaction rates per
unit volume. If the nuclide is not in the ISOTXS library, a warning is printed.
For each child of ``obj``, use the ISOTXS library and the cross-section ID for the associated block to produce a
reaction rate dictionary in units of inverse seconds for the nuclide specified in the original call to
``obj.getReactionRates()``. Because ``nDensity`` was originally specified as 1.0, this dictionary actually
represents the reaction rates per unit volume. If the nuclide is not in the ISOTXS library a warning is printed.

Combine the reaction rates for all nuclides into a combined dictionary by summing together
reaction rates of the same type on the same isotope from each of the children of ``obj``.
Combine the reaction rates for all nuclides into a combined dictionary by summing together reaction rates of the
same type on the same isotope from each of the children of ``obj``.

If ``obj`` has a non-zero multi-group flux, sum the group-wise flux into the total flux and
normalize the reaction rates by the total flux, producing a one-group macroscopic cross
section for each reaction type on each nuclide. Store these values in a
:py:class:`~armi.physics.neutronics.isotopicDepletion.crossSectionTable.CrossSectionTable`.
If ``obj`` has a non-zero multi-group flux, sum the group-wise flux into the total flux and normalize the
reaction rates by the total flux, producing a one-group macroscopic cross section for each reaction type on each
nuclide. Store these values in a ``CrossSectionTable``.

Parameters
----------
Expand All @@ -202,8 +193,8 @@ def makeReactionRateTable(obj, nuclides: List = None):

Notes
-----
This also used to do some caching on the block level but that has been removed
and the calls to this may therefore need to be re-optimized.
This also used to do some caching on the block level but that has been removed and the calls to this may therefore
need to be re-optimized.

See Also
--------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,23 @@ class AbstractIsotopicDepleter:
"""
Interact with a depletion code.

This interface and subClasses deplete under a flux defined outside this
interface
This interface and subClasses deplete under a flux defined outside this interface

The depletion in this analysis only depends on the flux, material vectors,
nuclear data and continuous source and loss objects.
The depletion in this analysis only depends on the flux, material vectors, nuclear data and continuous source and
loss objects.

The depleters derived from this abstract class use all the fission products
armi can handle -- i.e. do not form lumped fission products.
The depleters derived from this abstract class use all the fission products ARMI can handle -- i.e. do not form
lumped fission products.

_depleteByName contains a ARMI objects to deplete keyed by name.
The class attribute _depleteByName contains a ARMI objects to deplete keyed by name.

.. impl:: ARMI provides a base class to deplete isotopes.
:id: I_ARMI_DEPL_ABC
:implements: R_ARMI_DEPL_ABC

This class provides some basic infrastructure typically needed in depletion
calculations within the ARMI framework. It stores a reactor, operator,
and case settings object, and also defines methods to store and retrieve
the objects which should be depleted based on their names.
This class provides some basic infrastructure typically needed in depletion calculations within the ARMI
framework. It stores a reactor, operator, and case settings object, and also defines methods to store and
retrieve the objects which should be depleted based on their names.
"""

name = None
Expand All @@ -91,8 +89,7 @@ def __init__(self, r=None, cs=None, o=None):
self.cs = cs
self.o = o

# ARMI objects to deplete keyed by name
# order is important for consistency in iterating through objects
# ARMI objects to deplete keyed by name order is important for consistency in iterating through objects
self._depleteByName = collections.OrderedDict()

self.efpdToBurn = None
Expand All @@ -115,8 +112,7 @@ def run(self):
"""
Submit depletion case with external solver to the cluster.

In addition to running the physics kernel, this method calls the waitForJob method
to wait for it job to finish
In addition to running the physics kernel, this method calls the waitForJob method to wait for it job to finish.

comm = MPI.COMM_SELF.Spawn(sys.executable,args=['cpi.py'],maxprocs=5)
"""
Expand All @@ -137,18 +133,17 @@ def makeXsecTable(
Parameters
----------
armiObject: armiObject
an armi object -- batch or block --
with a .p.xsType and a getMgFlux method
an armi object -- batch or block -- with a .p.xsType and a getMgFlux method
activeNuclides: list
a list of the nucNames of active isotopes
isotxs: isotxs object
headerFormat: string (optional)
this is the format in which the elements of the header with be returned -- i.e. if you use a
.format() call with the case name you'll return a formatted list of string elements
this is the format in which the elements of the header with be returned -- i.e. if you use a .format() call with
the case name you'll return a formatted list of string elements
tableFormat: string (optional)
This is the format in which the elements of the table with be returned -- i.e. if you use a
.format() call with mcnpId, nG, nF, n2n, n3n, nA, and nP you'll get the format you want. If
you use a .format() call with the case name you'll return a formatted list of strings.
This is the format in which the elements of the table with be returned -- i.e. if you use a .format() call with
mcnpId, nG, nF, n2n, n3n, nA, and nP you'll get the format you want. If you use a .format() call with the case
name you'll return a formatted list of strings.

Results
-------
Expand Down Expand Up @@ -196,8 +191,7 @@ class Csrc:

Notes
-----
The chemical vector is a dictionary of chemicals and their removal rate constant -- this works
like a decay constant.
The chemical vector is a dictionary of chemicals and their removal rate constant. This works like a decay constant.

The isotopic vector is used to make a source material in continuous source definitions.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,14 @@ def _isTopDummyBlockPresent(self):
Notes
-----
- If true, then axial expansion will be physical for all blocks.
- If false, the top most block in the assembly is artificially chopped
to preserve the assembly height. A runLog.Warning also issued.
- If false, the top most block in the assembly is artificially chopped to preserve the assembly height. A
runLog.Warning also issued.
"""
self.topMostBlock = self.linked.a[-1]
if not self.topMostBlock.hasFlags(Flags.DUMMY):
runLog.warning(
f"No dummy block present at the top of {self.linked.a}! "
"Top most block will be artificially chopped "
"to preserve assembly height"
f"No dummy block present at the top of {self.linked.a}! Top most block will be artificially chopped to "
"preserve assembly height"
)
if self._detailedAxialExpansion:
msg = "Cannot run detailedAxialExpansion without a dummy block at the top of the assembly!"
Expand All @@ -344,24 +343,23 @@ def _isTopDummyBlockPresent(self):
def axiallyExpandAssembly(self, recalculateBurnup: bool = True):
"""Utilizes assembly linkage to do axial expansion.

Parameters
----------
recalculateBurnup
Optional parameter to skip the recalculate burnup step.

.. impl:: Preserve the total height of an ARMI assembly, during expansion.
:id: I_ARMI_ASSEM_HEIGHT_PRES
:implements: R_ARMI_ASSEM_HEIGHT_PRES

The total height of an Assembly is preserved by not changing the ``ztop`` position
of the top-most Block in an Assembly. The ``zbottom`` of the top-most Block is
adjusted to match the Block immediately below it. The ``height`` of the
top-most Block is is then updated to reflect any expansion/contraction.
The total height of an Assembly is preserved by not changing the ``ztop`` position of the top-most Block in
an Assembly. The ``zbottom`` of the top-most Block is adjusted to match the Block immediately below it. The
``height`` of the top-most Block is is then updated to reflect any expansion/contraction.

Parameters
----------
recalculateBurnup
Optional parameter to skip the recalculate burnup step.
"""
mesh = [0.0]
runLog.debug(
"Printing component expansion information (growth percentage and 'target component')"
f"for each block in assembly {self.linked.a}."
"Printing component expansion information (growth percentage and 'target component') for each block in "
f"assembly {self.linked.a}."
)
# expand all of the components
for b in self.linked.a:
Expand Down
13 changes: 13 additions & 0 deletions armi/reactor/converters/parameterSweeps/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2026 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.
Loading
Loading