Repository: terrapower/armi

Issue:
Improve `HexBlock.getFlowArea()` calculation
Right now, the `HexBlock.getFlowArea()` calculation only calculates the area based on `Flags.COOLANT`, but @clstocking reports it could also be dependent on `Flags.INTERDUCTCOOLANT`. 

From the current calc:

https://github.com/terrapower/armi/blob/6fcc48196842f3580496e152a6b2862b1ea75a91/armi/reactor/blocks.py#L2594

To something more like:

```python
area = self.getComponent(Flags.COOLANT, exact=True).getArea()
for c in self.getComponents(Flags.INTERDUCTCOOLANT, exact=True):
    area += c.getArea()

return area
```

Discussion:

Solution patch:
diff --git a/armi/physics/neutronics/globalFlux/globalFluxInterface.py b/armi/physics/neutronics/globalFlux/globalFluxInterface.py
index 283e02bd..e54d87b8 100644
--- a/armi/physics/neutronics/globalFlux/globalFluxInterface.py
+++ b/armi/physics/neutronics/globalFlux/globalFluxInterface.py
@@ -393,8 +393,7 @@ class GlobalFluxOptions(executers.ExecutionOptions):
     detailedAxialExpansion : bool
         Turn on detailed axial expansion? from settings
     dpaPerFluence : float
-        A quick and dirty conversion that is used to get dpaPeak by multiplying
-        the factor and fastFluencePeak
+        A quick and dirty conversion that is used to get dpaPeak
     energyDepoCalcMethodStep : str
         For gamma transport/normalization
     epsEigenvalue : float
diff --git a/armi/physics/neutronics/parameters.py b/armi/physics/neutronics/parameters.py
index 7913e152..1db7329b 100644
--- a/armi/physics/neutronics/parameters.py
+++ b/armi/physics/neutronics/parameters.py
@@ -18,7 +18,6 @@ Parameter definitions for the Neutronics Plugin.
 We hope neutronics plugins that compute flux will use ``mgFlux``, etc., which will enable modular
 construction of apps.
 """
-from armi.physics.neutronics.settings import CONF_DPA_PER_FLUENCE
 from armi.reactor import parameters
 from armi.reactor.blocks import Block
 from armi.reactor.parameters import ParamLocation
@@ -411,27 +410,6 @@ def _getNeutronicsBlockParams():
             saveToDB="True",
         )
 
-        pb.defParam(
-            "fastFluence",
-            units=f"#/{units.CM}^2",
-            description="Fast spectrum fluence",
-            categories=[
-                parameters.Category.cumulative,
-                parameters.Category.detailedAxialExpansion,
-            ],
-        )
-
-        pb.defParam(
-            "fastFluencePeak",
-            units=f"#/{units.CM}^2",
-            description="Fast spectrum fluence with a peaking factor",
-            location=ParamLocation.MAX,
-            categories=[
-                parameters.Category.cumulative,
-                parameters.Category.detailedAxialExpansion,
-            ],
-        )
-
         pb.defParam(
             "fluence",
             units=f"#/{units.CM}^2",
@@ -659,20 +637,6 @@ def _getNeutronicsBlockParams():
             categories=[parameters.Category.cumulative, parameters.Category.neutronics],
         )
 
-        pb.defParam(
-            "dpaPeakFromFluence",
-            units=units.DPA,
-            description=(
-                "DPA approximation based on a fluence conversion factor set in the "
-                f"{CONF_DPA_PER_FLUENCE} setting"
-            ),
-            location=ParamLocation.MAX,
-            categories=[
-                parameters.Category.cumulative,
-                parameters.Category.detailedAxialExpansion,
-            ],
-        )
-
         pb.defParam(
             "enrichmentBOL",
             units=units.UNITLESS,
diff --git a/armi/physics/neutronics/settings.py b/armi/physics/neutronics/settings.py
index 0dac5752..a4b79fb9 100644
--- a/armi/physics/neutronics/settings.py
+++ b/armi/physics/neutronics/settings.py
@@ -126,8 +126,7 @@ def defineSettings():
             CONF_DPA_PER_FLUENCE,
             default=4.01568627451e-22,
             label="DPA Per Fluence",
-            description="A quick and dirty conversion that is used to get "
-            "dpaPeak by multiplying the factor and fastFluencePeak",
+            description="A quick and dirty conversion that is used to get dpaPeak",
         ),
         setting.Setting(
             CONF_BC_COEFFICIENT,
diff --git a/armi/reactor/assemblyParameters.py b/armi/reactor/assemblyParameters.py
index f7f1e068..ed81cb5f 100644
--- a/armi/reactor/assemblyParameters.py
+++ b/armi/reactor/assemblyParameters.py
@@ -23,19 +23,6 @@ from armi.utils import units
 def getAssemblyParameterDefinitions():
     pDefs = parameters.ParameterDefinitionCollection()
 
-    with pDefs.createBuilder() as pb:
-
-        pb.defParam(
-            "powerDecay",
-            setter=isNumpyArray("powerDecay"),
-            units=units.WATTS,
-            description="List of decay heats at each time step specified in "
-            "decayHeatCalcTimesInSeconds setting.",
-            saveToDB=True,
-            location=ParamLocation.AVERAGE,
-            default=None,
-        )
-
     with pDefs.createBuilder(location=ParamLocation.CENTROID) as pb:
 
         pb.defParam(
@@ -138,9 +125,7 @@ def getAssemblyParameterDefinitions():
             description=(
                 "High-fidelity number density vector with up to thousands of nuclides. "
                 "Used in high-fi depletion runs where low-fi depletion may also be occurring. "
-                "This param keeps the hi-fi and low-fi depletion values from interfering. "
-                "See core.p.detailedNucKeys for keys. "
-                # Could be moved to external physics plugin
+                "This param keeps the hi-fi and low-fi depletion values from interfering."
             ),
             saveToDB=True,
             default=None,
diff --git a/armi/reactor/blockParameters.py b/armi/reactor/blockParameters.py
index 43450e35..8067167a 100644
--- a/armi/reactor/blockParameters.py
+++ b/armi/reactor/blockParameters.py
@@ -16,7 +16,7 @@
 from armi import runLog
 from armi.physics.neutronics import crossSectionGroupManager
 from armi.reactor import parameters
-from armi.reactor.parameters import NoDefault, Parameter, ParamLocation
+from armi.reactor.parameters import ParamLocation
 from armi.reactor.parameters.parameterDefinitions import isNumpyArray
 from armi.utils import units
 from armi.utils.units import ASCII_LETTER_A, ASCII_LETTER_Z, ASCII_LETTER_a
@@ -53,9 +53,7 @@ def getBlockParameterDefinitions():
             description=(
                 "High-fidelity number density vector with up to thousands of nuclides. "
                 "Used in high-fi depletion runs where low-fi depletion may also be occurring. "
-                "This param keeps the hi-fi and low-fi depletion values from interfering. "
-                "See core.p.detailedNucKeys for keys. "
-                # could move to external physic plugin
+                "This param keeps the hi-fi and low-fi depletion values from interfering."
             ),
             location=ParamLocation.AVERAGE,
             saveToDB=False,
@@ -66,13 +64,6 @@ def getBlockParameterDefinitions():
         default=0.0, location=ParamLocation.AVERAGE, categories=["depletion"]
     ) as pb:
 
-        pb.defParam(
-            "burnupMWdPerKg",
-            units=f"{units.MWD}/{units.KG}",
-            description="Burnup in MWd/kg of initial heavy metal",
-            categories=["cumulative"],
-        )
-
         pb.defParam(
             "newDPA",
             units=units.DPA,
@@ -95,14 +86,6 @@ def getBlockParameterDefinitions():
             location=ParamLocation.CHILDREN,
         )
 
-        pb.defParam(
-            "percentBuMax",
-            units=units.PERCENT_FIMA,
-            description="Maximum percentage in a single pin of the initial heavy metal "
-            "atoms that have been fissioned",
-            location=ParamLocation.MAX,
-        )
-
         pb.defParam(
             "residence",
             units=units.DAYS,
@@ -144,32 +127,6 @@ def getBlockParameterDefinitions():
             ),
         )
 
-    pDefs.add(
-        Parameter(
-            name="depletionMatrix",
-            units=units.UNITLESS,
-            description="Full BurnMatrix objects containing transmutation and decay info about this block.",
-            location=ParamLocation.AVERAGE,
-            saveToDB=False,
-            default=None,
-            setter=NoDefault,
-            categories=set(),
-        )
-    )
-
-    pDefs.add(
-        Parameter(
-            name="cycleAverageBurnMatrix",
-            units=units.UNITLESS,
-            description="Integrated burn matrix mapping this block from its BOC to EOC number densities.",
-            location=ParamLocation.AVERAGE,
-            saveToDB=False,
-            default=None,
-            setter=NoDefault,
-            categories=set(),
-        )
-    )
-
     with pDefs.createBuilder(default=0.0, location=ParamLocation.AVERAGE) as pb:
 
         def envGroup(self, envGroupChar):
@@ -349,10 +306,9 @@ def getBlockParameterDefinitions():
             "topIndex",
             units=units.UNITLESS,
             description=(
-                "the axial block index within its parent assembly (0 is bottom block). This index with"
-                "regard to the mesh of the reference assembly so it does not increase by 1 for each block."
-                "It is used to keep the mesh of the assemblies uniform with axial expansion."
-                "See setBlockMesh, makeAxialSnapList",
+                "the axial block index within its parent assembly (0 is bottom block). This index with regard to the "
+                "mesh of the reference assembly so it does not increase by 1 for each block. It is used to keep the "
+                "mesh of the assemblies uniform with axial expansion. See setBlockMesh, makeAxialSnapList",
             ),
             default=0,
             saveToDB=True,
@@ -363,14 +319,7 @@ def getBlockParameterDefinitions():
             "eqRegion",
             units=units.UNITLESS,
             description="Equilibrium shuffling region. Corresponds to how many full cycles fuel here has gone through.",
-            default=-1,
-        )
-
-        pb.defParam(
-            "eqCascade",
-            units=units.UNITLESS,
-            description="Cascade number in repetitive equilibrium shuffling fuel management.",
-            default=-1,
+            default=0.0,
         )
 
         pb.defParam(
@@ -445,27 +394,6 @@ def getBlockParameterDefinitions():
             location=ParamLocation.AVERAGE,
         )
 
-        pb.defParam(
-            "fissileDestroyed",
-            units=f"atoms/(bn*{units.CM})",
-            description="Fissile atoms destroyed in last depletion step (not net!)",
-            location=ParamLocation.AVERAGE,
-        )
-
-        pb.defParam(
-            "fissileBefore",
-            units=f"atoms/(bn*{units.CM})",
-            description="Fissile atoms at beginning of last depletion step (could be substep!)",
-            location=ParamLocation.AVERAGE,
-        )
-
-        pb.defParam(
-            "fissileAfter",
-            units=f"atoms/(bn*{units.CM})",
-            description="Fissile atoms at end of last depletion step (could be substep!)",
-            location=ParamLocation.AVERAGE,
-        )
-
         pb.defParam("buLimit", units=units.PERCENT_FIMA, description="Burnup limit")
 
         pb.defParam(
@@ -476,13 +404,6 @@ def getBlockParameterDefinitions():
             location=ParamLocation.AVERAGE,
         )
 
-        pb.defParam(
-            "cyclicNErr",
-            units=units.UNITLESS,
-            description="Relative error of the block number density",
-            location=ParamLocation.AVERAGE,
-        )
-
         pb.defParam(
             "heightBOL",
             units=units.CM,
@@ -514,13 +435,6 @@ def getBlockParameterDefinitions():
 
         pb.defParam("nPins", units=units.UNITLESS, description="Number of pins")
 
-        pb.defParam(
-            "newDPAPeak",
-            units=units.DPA,
-            description="The peak DPA accumulated in the last burn step",
-            location=ParamLocation.MAX,
-        )
-
         pb.defParam(
             "percentBuPeak",
             units=units.PERCENT_FIMA,
diff --git a/armi/reactor/blocks.py b/armi/reactor/blocks.py
index a3f04b18..d9350a86 100644
--- a/armi/reactor/blocks.py
+++ b/armi/reactor/blocks.py
@@ -105,20 +105,6 @@ class Block(composites.Composite):
         for problemParam in ["THcornTemp", "THedgeTemp"]:
             self.p[problemParam] = []
 
-        for problemParam in [
-            "residence",
-            "bondRemoved",
-            "fluence",
-            "fastFluence",
-            "fastFluencePeak",
-            "displacementX",
-            "displacementY",
-            "fluxAdj",
-            "buRate",
-            "eqRegion",
-        ]:
-            self.p[problemParam] = 0.0
-
     def __repr__(self):
         # be warned, changing this might break unit tests on input file generations
         return "<{type} {name} at {loc} XS: {xs} ENV GP: {env}>".format(
@@ -235,16 +221,12 @@ class Block(composites.Composite):
         circles = self.getComponentsOfShape(components.Circle)
         if not circles:
             raise ValueError(
-                "Cannot get smear density of {}. There are no circular components.".format(
-                    self
-                )
+                f"Cannot get smear density of {self}. There are no circular components."
             )
         clads = set(self.getComponents(Flags.CLAD)).intersection(set(circles))
         if not clads:
             raise ValueError(
-                "Cannot get smear density of {}. There are no clad components.".format(
-                    self
-                )
+                f"Cannot get smear density of {self}. There are no clad components."
             )
 
         # Compute component areas
@@ -278,8 +260,8 @@ class Block(composites.Composite):
                     negativeArea += abs(componentArea)
         if cold and negativeArea:
             raise ValueError(
-                "Negative component areas exist on {}. Check that the cold dimensions are properly aligned "
-                "and no components overlap.".format(self)
+                f"Negative component areas exist on {self}. Check that the cold dimensions are "
+                "properly aligned and no components overlap."
             )
         innerCladdingArea += negativeArea  # See note 2
         totalMovableArea = innerCladdingArea - unmovableComponentArea
@@ -390,17 +372,16 @@ class Block(composites.Composite):
         """
         Returns the microscopic library suffix (e.g. 'AB') for this block.
 
-        DIF3D and MC2 are limited to 6 character nuclide labels. ARMI by convention uses
-        the first 4 for nuclide name (e.g. U235, PU39, etc.) and then uses the 5th
-        character for cross-section type and the 6th for burnup group. This allows a
-        variety of XS sets to be built modeling substantially different blocks.
+        DIF3D and MC2 are limited to 6 character nuclide labels. ARMI by convention uses the first 4
+        for nuclide name (e.g. U235, PU39, etc.) and then uses the 5th character for cross-section
+        type and the 6th for burnup group. This allows a variety of XS sets to be built modeling
+        substantially different blocks.
 
         Notes
         -----
-        The single-letter use for xsType and envGroup limit users to 52 groups of each.
-        ARMI will allow 2-letter xsType designations if and only if the `envGroup`
-        setting has length 1 (i.e. no burnup/temp groups are defined). This is useful for
-        high-fidelity XS modeling of V&V models such as the ZPPRs.
+        The single-letter use for xsType and envGroup limit users to 52 groups of each. ARMI will
+        allow 2-letter xsType designations if and only if the `envGroup` setting has length 1 (i.e.
+        no burnup/temp groups are defined). This is useful for high-fidelity XS modeling.
         """
         env = self.p.envGroup
         if not env:
@@ -438,21 +419,15 @@ class Block(composites.Composite):
             Conserve mass of nuclides in ``adjustList``.
 
         adjustList : list, optional
-            Nuclides that will be conserved in conserving mass in the block. It is recommended to pass a list of
-            all nuclides in the block.
+            Nuclides that will be conserved in conserving mass in the block. It is recommended to
+            pass a list of all nuclides in the block.
 
         Notes
         -----
-        There is a coupling between block heights, the parent assembly axial mesh,
-        and the ztop/zbottom/z params of the sibling blocks. When you set a height,
-        all those things are invalidated. Thus, this method has to go through and
-        update them via ``parent.calculateZCoords``. This could be inefficient
-        though it has not been identified as a bottleneck. Possible improvements
-        include deriving z/ztop/zbottom on the fly and invalidating the parent mesh
-        with some kind of flag, signaling it to recompute itself on demand.
-        Developers can get around some of the O(N^2) scaling of this by setting
-        ``p.height`` directly but they must know to update the dependent objects
-        after they do that. Use with care.
+        There is a coupling between block heights, the parent assembly axial mesh, and the
+        ztop/zbottom/z params of the sibling blocks. When you set a height, all those things are
+        invalidated. Thus, this method has to go through and update them via
+        ``parent.calculateZCoords``.
 
         See Also
         --------
@@ -464,9 +439,7 @@ class Block(composites.Composite):
         originalHeight = self.getHeight()  # get before modifying
         if modifiedHeight < 0.0:
             raise ValueError(
-                "Cannot set height of block {} to height of {} cm".format(
-                    self, modifiedHeight
-                )
+                f"Cannot set height of block {self} to height of {modifiedHeight} cm"
             )
         self.p.height = modifiedHeight
         self.clearCache()
@@ -499,8 +472,8 @@ class Block(composites.Composite):
             return self.getComponent(Flags.COOLANT, exact=True).getArea() / numPins
         except ZeroDivisionError:
             raise ZeroDivisionError(
-                "Block {} has 0 pins (fuel, clad, control, shield, etc.). Thus, its flow area "
-                "per pin is undefined.".format(self)
+                f"Block {self} has 0 pins (fuel, clad, control, shield, etc.). Thus, its flow area "
+                "per pin is undefined."
             )
 
     def getHydraulicDiameter(self):
@@ -517,8 +490,8 @@ class Block(composites.Composite):
 
         Notes
         -----
-        completeInitialLoading must be run because adjusting the enrichment actually
-        changes the mass slightly and you can get negative burnups, which you do not want.
+        completeInitialLoading must be run because adjusting the enrichment actually changes the
+        mass slightly and you can get negative burnups, which you do not want.
         """
         fuels = self.getChildrenWithFlags(Flags.FUEL)
 
@@ -541,16 +514,15 @@ class Block(composites.Composite):
             :id: I_ARMI_BLOCK_POSI0
             :implements: R_ARMI_BLOCK_POSI
 
-            If the block does not have its ``core`` attribute set, if the block's
-            parent does not have a ``spatialGrid`` attribute, or if the block
-            does not have its location defined by its ``spatialLocator`` attribute,
-            return a string indicating that it is outside of the core.
+            If the block does not have its ``core`` attribute set, if the block's parent does not
+            have a ``spatialGrid`` attribute, or if the block does not have its location defined by
+            its ``spatialLocator`` attribute, return a string indicating that it is outside of the
+            core.
 
-            Otherwise, use the :py:class:`~armi.reactor.grids.Grid.getLabel` static
-            method to convert the block's indices into a string like "XXX-YYY-ZZZ".
-            For hexagonal geometry, "XXX" is the zero-padded hexagonal core ring,
-            "YYY" is the zero-padded position in that ring, and "ZZZ" is the zero-padded
-            block axial index from the bottom of the core.
+            Otherwise, use the :py:class:`~armi.reactor.grids.Grid.getLabel` static method to
+            convert the block's indices into a string like "XXX-YYY-ZZZ". For hexagonal geometry,
+            "XXX" is the zero-padded hexagonal core ring, "YYY" is the zero-padded position in that
+            ring, and "ZZZ" is the zero-padded block axial index from the bottom of the core.
         """
         if self.core and self.parent.spatialGrid and self.spatialLocator:
             return self.core.spatialGrid.getLabel(
@@ -568,9 +540,9 @@ class Block(composites.Composite):
             :implements: R_ARMI_BLOCK_POSI
 
             Calls to the :py:meth:`~armi.reactor.grids.locations.IndexLocation.getGlobalCoordinates`
-            method of the block's ``spatialLocator`` attribute, which recursively
-            calls itself on all parents of the block to get the coordinates of the
-            block's centroid in 3D cartesian space.
+            method of the block's ``spatialLocator`` attribute, which recursively calls itself on
+            all parents of the block to get the coordinates of the block's centroid in 3D cartesian
+            space.
         """
         return self.spatialLocator.getGlobalCoordinates()
 
@@ -592,11 +564,10 @@ class Block(composites.Composite):
         """
         Return the area of a block for a full core or a 1/3 core model.
 
-        Area is consistent with the area in the model, so if you have a central
-        assembly in a 1/3 symmetric model, this will return 1/3 of the total
-        area of the physical assembly. This way, if you take the sum
-        of the areas in the core (or count the atoms in the core, etc.),
-        you will have the proper number after multiplying by the model symmetry.
+        Area is consistent with the area in the model, so if you have a central assembly in a 1/3
+        symmetric model, this will return 1/3 of the total area of the physical assembly. This way,
+        if you take the sum of the areas in the core (or count the atoms in the core, etc.), you
+        will have the proper number after multiplying by the model symmetry.
 
         Parameters
         ----------
@@ -616,8 +587,8 @@ class Block(composites.Composite):
         armi.reactor.blocks.Block.getMaxArea
             return the full area of the physical assembly disregarding model symmetry
         """
-        # this caching requires that you clear the cache every time you adjust anything
-        # including temperature and dimensions.
+        # this caching requires that you clear the cache every time you adjust anything including
+        # temperature and dimensions.
         area = self._getCached("area")
         if area:
             return area
@@ -654,10 +625,9 @@ class Block(composites.Composite):
 
         Takes into account assemblies that are bisected or trisected by symmetry lines
 
-        In 1/3 symmetric cases, the central assembly is 1/3 a full area.
-        If edge assemblies are included in a model, the symmetry factor along
-        both edges for overhanging assemblies should be 2.0. However,
-        ARMI runs in most scenarios with those assemblies on the 120-edge removed,
+        In 1/3 symmetric cases, the central assembly is 1/3 a full area. If edge assemblies are
+        included in a model, the symmetry factor along both edges for overhanging assemblies should
+        be 2.0. However, ARMI runs in most scenarios with those assemblies on the 120-edge removed,
         so the symmetry factor should generally be just 1.0.
 
         See Also
@@ -718,29 +688,27 @@ class Block(composites.Composite):
         is perturbed, all of htem are perturbed.
         """
         if self.p.detailedNDens is None:
-            # BOL assems get expanded to a reference so the first check is needed so it
-            # won't call .blueprints on None since BOL assems don't have a core/r
+            # BOL assems get expanded to a reference so the first check is needed so it won't call
+            # .blueprints on None since BOL assems don't have a core/r
             return
         if any(nuc in self.core.r.blueprints.activeNuclides for nuc in adjustList):
             self.p.detailedNDens *= frac
-            # Other power densities do not need to be updated as they are calculated in
-            # the global flux interface, which occurs after axial expansion from crucible
-            # on the interface stack.
+            # Other power densities do not need to be updated as they are calculated in the global
+            # flux interface, which occurs after axial expansion on the interface stack.
             self.p.pdensDecay *= frac
 
     def completeInitialLoading(self, bolBlock=None):
         """
         Does some BOL bookkeeping to track things like BOL HM density for burnup tracking.
 
-        This should run after this block is loaded up at BOC (called from
-        Reactor.initialLoading).
+        This should run after this block is loaded up at BOC (called from Reactor.initialLoading).
 
-        The original purpose of this was to get the moles HM at BOC for the moles
-        Pu/moles HM at BOL calculation.
+        The original purpose of this was to get the moles HM at BOC for the moles Pu/moles HM at BOL
+        calculation.
 
-        This also must be called after modifying something like the smear density or zr
-        fraction in an optimization case. In ECPT cases, a BOL block must be passed or
-        else the burnup will try to get based on a pre-burned value.
+        This also must be called after modifying something like the smear density or zr fraction in
+        an optimization case. In ECPT cases, a BOL block must be passed or else the burnup will try
+        to get based on a pre-burned value.
 
         Parameters
         ----------
@@ -779,8 +747,8 @@ class Block(composites.Composite):
         for child in self:
             hmMass = child.getHMMass()
             massHmBOL += hmMass
-            # Components have the following parameters but not every composite will
-            # massHmBOL, molesHmBOL, puFrac
+            # Components have the following parameters but not every composite will massHmBOL,
+            # molesHmBOL, puFrac
             if isinstance(child, components.Component):
                 child.p.massHmBOL = hmMass
                 child.p.molesHmBOL = child.getHMMoles()
@@ -809,23 +777,21 @@ class Block(composites.Composite):
         if not b10Comps:
             return
 
-        # get the highest density comp dont want to sum all because some
-        # comps might have very small impurities of boron and adding this
-        # volume won't be conservative for captures per cc.
+        # get the highest density comp dont want to sum all because some comps might have very small
+        # impurities of boron and adding this volume won't be conservative for captures per cc.
         b10Comp = sorted(b10Comps, key=lambda x: x.getNumberDensity("B10"))[-1]
 
         if len(b10Comps) > 1:
             runLog.warning(
-                f"More than one boron10-containing component found  in {self.name}. "
-                f"Only {b10Comp} will be considered for calculation of initialB10ComponentVol "
-                "Since adding multiple volumes is not conservative for captures/cc."
-                f"All compos found {b10Comps}",
+                f"More than one boron10-containing component found in {self.name}. Only {b10Comp} "
+                f"will be considered for calculation of initialB10ComponentVol Since adding "
+                f"multiple volumes is not conservative for captures. All compos found {b10Comps}",
                 single=True,
             )
         if self.isFuel():
             runLog.warning(
-                f"{self.name} has both fuel and initial b10. "
-                "b10 volume may not be conserved with axial expansion.",
+                f"{self.name} has both fuel and initial b10. b10 volume may not be conserved with "
+                "axial expansion.",
                 single=True,
             )
 
@@ -863,12 +829,10 @@ class Block(composites.Composite):
 
     @staticmethod
     def plotFlux(core, fName=None, bList=None, peak=False, adjoint=False, bList2=[]):
-        # Block.plotFlux has been moved to utils.plotting as plotBlockFlux, which is a
-        # better fit.
-        # We don't want to remove the plotFlux function in the Block namespace yet
-        # in case client code is depending on this function existing here. This is just
-        # a simple pass-through function that passes the arguments along to the actual
-        # implementation in its new location.
+        # Block.plotFlux has been moved to utils.plotting as plotBlockFlux, which is a better fit.
+        # We don't want to remove the plotFlux function in the Block namespace yet in case client
+        # code is depending on this function existing here. This is just a simple pass-through
+        # function that passes the arguments along to the actual implementation in its new location.
         plotBlockFlux(core, fName, bList, peak, adjoint, bList2)
 
     def _updatePitchComponent(self, c):
@@ -876,9 +840,8 @@ class Block(composites.Composite):
         Update the component that defines the pitch.
 
         Given a Component, compare it to the current component that defines the pitch of the Block.
-        If bigger, replace it.
-        We need different implementations of this to support different logic for determining the
-        form of pitch and the concept of "larger".
+        If bigger, replace it. We need different implementations of this to support different logic
+        for determining the form of pitch and the concept of "larger".
 
         See Also
         --------
@@ -936,10 +899,11 @@ class Block(composites.Composite):
 
     def getComponentsThatAreLinkedTo(self, comp, dim):
         """
-        Determine which dimensions of which components are linked to a specific dimension of a particular component.
+        Determine which dimensions of which components are linked to a specific dimension of a
+        particular component.
 
-        Useful for breaking fuel components up into individuals and making sure
-        anything that was linked to the fuel mult (like the cladding mult) stays correct.
+        Useful for breaking fuel components up into individuals and making sure anything that was
+        linked to the fuel mult (like the cladding mult) stays correct.
 
         Parameters
         ----------
@@ -1022,11 +986,10 @@ class Block(composites.Composite):
 
         Notes
         -----
-        If you just want sorted components in this block, use ``sorted(self)``.
-        This will never include any ``DerivedShape`` objects. Since they have a derived
-        area they don't have a well-defined dimension. For now we just ignore them.
-        If they are desired in the future some knowledge of their dimension will be
-        required while they are being derived.
+        If you just want sorted components in this block, use ``sorted(self)``. This will never
+        include any ``DerivedShape`` objects. Since they have a derived area they don't have a well-
+        defined dimension. For now we just ignore them. If they are desired in the future some
+        knowledge of their dimension will be required while they are being derived.
         """
         sortedComponents = sorted(self)
         componentIndex = sortedComponents.index(component)
@@ -1042,15 +1005,13 @@ class Block(composites.Composite):
 
             Uses some simple criteria to infer the number of pins in the block.
 
-            For every flag in the module list :py:data:`~armi.reactor.blocks.PIN_COMPONENTS`,
-            loop over all components of that type in the block. If the component
-            is an instance of :py:class:`~armi.reactor.components.basicShapes.Circle`,
-            add its multiplicity to a list, and sum that list over all components
-            with each given flag.
+            For every flag in the module list :py:data:`~armi.reactor.blocks.PIN_COMPONENTS`, loop
+            over all components of that type in the block. If the component is an instance of
+            :py:class:`~armi.reactor.components.basicShapes.Circle`, add its multiplicity to a list,
+            and sum that list over all components with each given flag.
 
-            After looping over all possibilities, return the maximum value returned
-            from the process above, or if no compatible components were found,
-            return zero.
+            After looping over all possibilities, return the maximum value returned from the process
+            above, or if no compatible components were found, return zero.
         """
         nPins = [
             sum(
@@ -1085,7 +1046,7 @@ class Block(composites.Composite):
         This merges on a high level (using number densities). Components will not be merged.
 
         This is used e.g. for inserting a control block partially to get a very tight criticality
-        control.  In this case, a control block would be merged with a duct block. It is also used
+        control. In this case, a control block would be merged with a duct block. It is also used
         when a control rod is specified as a certain length but that length does not fit exactly
         into a full block.
         """
@@ -1234,8 +1195,7 @@ class Block(composites.Composite):
         if c is None:
             raise ValueError("{} has no valid pitch defining component".format(self))
 
-        # ask component for dimensions, since they could have changed,
-        # due to temperature, for example.
+        # ask component for dimensions, since they could have changed due to temperature
         p = c.getPitchData()
         return (p, c) if returnComp else p
 
@@ -1295,16 +1255,15 @@ class Block(composites.Composite):
 
         This sets the settingPitch and actually sets the dimension of the outer hexagon.
 
-        During a load (importGeom), the setDimension doesn't usually do anything except
-        set the setting See Issue 034
+        During a load (importGeom), the setDimension doesn't usually do anything except set the
+        setting See Issue 034
 
-        But during a actual case modification (e.g. in an optimization sweep, then the dimension
-        has to be set as well.
+        But during a actual case modification (e.g. in an optimization sweep, then the dimension has
+        to be set as well.
 
         See Also
         --------
         getPitch : gets the pitch
-
         """
         c, _p = self._pitchDefiningComponent
         if c:
@@ -1377,10 +1336,9 @@ class Block(composites.Composite):
 
     def getBlocks(self):
         """
-        This method returns all the block(s) included in this block
-        its implemented so that methods could iterate over reactors, assemblies
-        or single blocks without checking to see what the type of the
-        reactor-family object is.
+        This method returns all the block(s) included in this block its implemented so that methods
+        could iterate over reactors, assemblies or single blocks without checking to see what the
+        type of the reactor-family object is.
         """
         return [self]
 
@@ -1390,10 +1348,9 @@ class Block(composites.Composite):
 
         Notes
         -----
-        This is VERY useful for defining a ThRZ core out of
-        differentialRadialSegements whose dimensions are connected together
-        some of these dimensions are derivative and can be updated by changing
-        dimensions in a Parameter Component or other linked components
+        This is VERY useful for defining a ThRZ core out of differentialRadialSegements whose
+        dimensions are connected together some of these dimensions are derivative and can be updated
+        by changing dimensions in a Parameter Component or other linked components
 
         See Also
         --------
@@ -1411,8 +1368,8 @@ class Block(composites.Composite):
         """
         Return the volume integrated multigroup neutron tracklength in [n-cm/s].
 
-        The first entry is the first energy group (fastest neutrons). Each additional
-        group is the next energy group, as set in the ISOTXS library.
+        The first entry is the first energy group (fastest neutrons). Each additional group is the
+        next energy group, as set in the ISOTXS library.
 
         Parameters
         ----------
@@ -1550,8 +1507,7 @@ class Block(composites.Composite):
         """
         Get the fission energy generation group constants for a block.
 
-        Gives the fission energy generation rates when multiplied by the multigroup
-        flux.
+        Gives the fission energy generation rates when multiplied by the multigroup flux.
 
         Returns
         -------
@@ -1565,8 +1521,8 @@ class Block(composites.Composite):
         """
         if not self.core.lib:
             raise RuntimeError(
-                "Cannot compute energy generation group constants without a library"
-                ". Please ensure a library exists."
+                "Cannot compute energy generation group constants without a library. Please ensure "
+                "a library exists."
             )
 
         return xsCollections.computeFissionEnergyGenerationConstants(
@@ -1577,8 +1533,7 @@ class Block(composites.Composite):
         """
         Get the capture energy generation group constants for a block.
 
-        Gives the capture energy generation rates when multiplied by the multigroup
-        flux.
+        Gives the capture energy generation rates when multiplied by the multigroup flux.
 
         Returns
         -------
@@ -2036,8 +1991,8 @@ class HexBlock(Block):
         except ValueError:
             # there are probably more that one clad/wire, so we really dont know what this block looks like
             runLog.info(
-                "Block design {} is too complicated to verify dimensions. Make sure they "
-                "are correct!".format(self)
+                f"Block design {self} is too complicated to verify dimensions. Make sure they are "
+                "correct!"
             )
             return
 
@@ -2413,10 +2368,14 @@ class HexBlock(Block):
 
     def getFlowArea(self):
         """Return the total flowing coolant area of the block in cm^2."""
-        return self.getComponent(Flags.COOLANT, exact=True).getArea()
+        area = self.getComponent(Flags.COOLANT, exact=True).getArea()
+        for c in self.getComponents(Flags.INTERDUCTCOOLANT, exact=True):
+            area += c.getArea()
+
+        return area
 
     def getHydraulicDiameter(self):
-        r"""
+        """
         Return the hydraulic diameter in this block in cm.
 
         Hydraulic diameter is 4A/P where A is the flow area and P is the wetted perimeter. In a hex
@@ -2466,8 +2425,6 @@ class CartesianBlock(Block):
 
 
 class ThRZBlock(Block):
-    # be sure to fill ThRZ blocks with only 3D components - components with explicit getVolume methods
-
     def getMaxArea(self):
         """Return the area of the Theta-R-Z block if it was totally full."""
         raise NotImplementedError(
diff --git a/armi/reactor/components/componentParameters.py b/armi/reactor/components/componentParameters.py
index 7ad05911..176b220c 100644
--- a/armi/reactor/components/componentParameters.py
+++ b/armi/reactor/components/componentParameters.py
@@ -23,6 +23,7 @@ def getComponentParameterDefinitions():
     """Return the base Component parameters."""
     pDefs = parameters.ParameterDefinitionCollection()
     with pDefs.createBuilder(location=ParamLocation.AVERAGE, saveToDB=True) as pb:
+
         pb.defParam(
             "volume", units=f"{units.CM}^3", description="Volume of this object."
         )
@@ -71,8 +72,7 @@ def getComponentParameterDefinitions():
             description=(
                 "High-fidelity number density vector with up to thousands of nuclides. "
                 "Used in high-fi depletion runs where low-fi depletion may also be occurring. "
-                "This param keeps the hi-fi and low-fi depletion values from interfering. "
-                "See core.p.detailedNucKeys for keys."
+                "This param keeps the hi-fi and low-fi depletion values from interfering."
             ),
             saveToDB=True,
             default=None,
@@ -121,14 +121,6 @@ def getComponentParameterDefinitions():
             default=None,
         )
 
-        pb.defParam(
-            "burnupMWdPerKg",
-            units=f"{units.MWD}/{units.KG}",
-            description="Burnup in MWd/Kg of heavy metal",
-            default=0.0,
-            categories=["cumulative"],
-        )
-
         pb.defParam(
             "customIsotopicsName",
             units=units.UNITLESS,
diff --git a/armi/reactor/cores.py b/armi/reactor/cores.py
index cfeb74e6..860b65ed 100644
--- a/armi/reactor/cores.py
+++ b/armi/reactor/cores.py
@@ -2140,7 +2140,6 @@ class Core(composites.Composite):
                 continue
 
         # add maxes based on pin-level max if it exists, block level max otherwise.
-        # may want to use percentBuMax for pin-detailed cases.
         self.p.maxBuF = max(
             (
                 a.getMaxParam("percentBu")
diff --git a/armi/reactor/reactorParameters.py b/armi/reactor/reactorParameters.py
index 0a2abc2d..e80d4098 100644
--- a/armi/reactor/reactorParameters.py
+++ b/armi/reactor/reactorParameters.py
@@ -15,7 +15,6 @@
 """Reactor parameter definitions."""
 from armi.reactor import parameters
 from armi.reactor.parameters import ParamLocation
-from armi.reactor.parameters.parameterDefinitions import isNumpyArray
 from armi.utils import units
 
 
@@ -80,18 +79,6 @@ def defineReactorParameters():
 def defineCoreParameters():
     pDefs = parameters.ParameterDefinitionCollection()
 
-    with pDefs.createBuilder() as pb:
-        pb.defParam(
-            "detailedNucKeys",
-            setter=isNumpyArray("detailedNucKeys"),
-            units=units.UNITLESS,
-            description="""Nuclide vector keys, used to map densities in b.p.detailedNDens and 
-            a.p.detailedNDens.ZZZAAA (ZZZ atomic number, AAA mass number, + 100 * m for metastable
-            states.""",
-            saveToDB=True,
-            default=None,
-        )
-
     with pDefs.createBuilder(location=ParamLocation.CENTROID) as pb:
         pb.defParam(
             "orientation",
@@ -163,12 +150,6 @@ def defineCoreParameters():
             default=None,
         )
 
-        pb.defParam(
-            "critSearchSlope",
-            units=f"1/{units.DAYS}",
-            description="Critical keff search slope",
-        )
-
         pb.defParam(
             "fissileMass", units=units.GRAMS, description="Fissile mass of the reactor"
         )
@@ -187,24 +168,6 @@ def defineCoreParameters():
             description="Uncontrolled k-effective for the reactor core (with control rods fully removed).",
         )
 
-        pb.defParam(
-            "lastKeff",
-            units=units.UNITLESS,
-            description="Previously calculated Keff for potential keff convergence",
-        )
-
-        pb.defParam(
-            "loadPadDpaAvg",
-            units=units.DPA,
-            description="The highest average dpa in any load pad",
-        )
-
-        pb.defParam(
-            "loadPadDpaPeak",
-            units=units.DPA,
-            description="The peak dpa in any load pad",
-        )
-
         pb.defParam(
             "maxDPA",
             units=units.DPA,
@@ -237,12 +200,6 @@ def defineCoreParameters():
             description="Grid plate peak dpa after 60 years irradiation",
         )
 
-        pb.defParam(
-            "totalIntrinsicSource",
-            units=f"n/{units.SECONDS}",
-            description="Full core intrinsic neutron source from spontaneous fissions before a decay period",
-        )
-
     with pDefs.createBuilder(
         location=ParamLocation.AVERAGE, default=0.0, categories=["neutronics"]
     ) as pb:
@@ -262,12 +219,6 @@ def defineCoreParameters():
             "and this will entirely overridden by that.",
         )
 
-        pb.defParam(
-            "powerDecay",
-            units=units.WATTS,
-            description="Decay power from decaying radionuclides",
-        )
-
         pb.defParam(
             "medAbsCore",
             units=units.EV,
@@ -462,24 +413,10 @@ def defineCoreParameters():
             default=0,
         )
 
-        pb.defParam(
-            "maxCyclicNErr",
-            units=units.UNITLESS,
-            description="Maximum relative number density error",
-            default=0.0,
-        )
-
     with pDefs.createBuilder(
         location=ParamLocation.AVERAGE, categories=["equilibrium"]
     ) as pb:
 
-        pb.defParam(
-            "breedingRatio",
-            units=units.UNITLESS,
-            description="Breeding ratio of the reactor",
-            default=0.0,
-        )
-
         pb.defParam(
             "ConvRatioCore",
             units=units.UNITLESS,


Test patch:
diff --git a/armi/reactor/tests/test_assemblies.py b/armi/reactor/tests/test_assemblies.py
index e586a79f..1a24f629 100644
--- a/armi/reactor/tests/test_assemblies.py
+++ b/armi/reactor/tests/test_assemblies.py
@@ -700,8 +700,6 @@ class Assembly_TestCase(unittest.TestCase):
     def test_getBlockData(self):
         paramDict = {
             "timeToLimit": 40.0,
-            "fastFluence": 1.01,
-            "fastFluencePeak": 50.0,
             "power": 10000.0,
             "envGroup": 4,
             "residence": 3.145,
diff --git a/armi/reactor/tests/test_blocks.py b/armi/reactor/tests/test_blocks.py
index 45539752..c07de8a1 100644
--- a/armi/reactor/tests/test_blocks.py
+++ b/armi/reactor/tests/test_blocks.py
@@ -995,9 +995,33 @@ class Block_TestCase(unittest.TestCase):
         self.assertAlmostEqual(cur, ref)
 
     def test_getFlowArea(self):
-        area = self.block.getComponent(Flags.COOLANT).getArea()
+        """Test Block.getFlowArea() for a Block with just coolant."""
+        ref = self.block.getComponent(Flags.COOLANT).getArea()
         cur = self.block.getFlowArea()
-        ref = area
+        self.assertAlmostEqual(cur, ref)
+
+    def test_getFlowAreaInterDuctCoolant(self):
+        """Test Block.getFlowArea() for a Block with coolant and interductcoolant."""
+        # build a test block with a Hex inter duct collant
+        fuelDims = {"Tinput": 400, "Thot": 400, "od": 0.76, "id": 0.00, "mult": 127.0}
+        ductDims = {"Tinput": 400, "Thot": 400, "op": 16, "ip": 15.3, "mult": 1.0}
+        coolDims = {"Tinput": 400, "Thot": 400}
+        iCoolantDims = {"Tinput": 400, "Thot": 400, "op": 17.0, "ip": 16, "mult": 1.0}
+
+        fuel = components.Circle("fuel", "UZr", **fuelDims)
+        duct = components.Hexagon("inner duct", "HT9", **ductDims)
+        coolant = components.DerivedShape("coolant", "Sodium", **coolDims)
+        iCoolant = components.Hexagon("interductcoolant", "Sodium", **iCoolantDims)
+
+        b = blocks.HexBlock("fuel", height=10.0)
+        b.add(fuel)
+        b.add(coolant)
+        b.add(duct)
+        b.add(iCoolant)
+
+        ref = b.getComponent(Flags.COOLANT).getArea()
+        ref += b.getComponent(Flags.INTERDUCTCOOLANT).getArea()
+        cur = b.getFlowArea()
         self.assertAlmostEqual(cur, ref)
 
     def test_getHydraulicDiameter(self):
diff --git a/armi/tests/anl-afci-177/anl-afci-177-fuelManagement.py b/armi/tests/anl-afci-177/anl-afci-177-fuelManagement.py
index cc608e6e..b1b41a7b 100644
--- a/armi/tests/anl-afci-177/anl-afci-177-fuelManagement.py
+++ b/armi/tests/anl-afci-177/anl-afci-177-fuelManagement.py
@@ -22,9 +22,8 @@ class SampleShuffler(FuelHandler):
             self.r.p.cycleLength * self.r.p.availabilityFactor * units.SECONDS_PER_DAY
         )
         for a in self.r.core:
-            peakFluence = a.getMaxParam("fastFluence")
             peakFlux = a.getMaxParam("fastFlux")
-            if peakFluence + peakFlux * cycleSeconds > 4.0e23:
+            if peakFlux * cycleSeconds > 4.0e23:
                 newAssem = self.r.core.createAssemblyOfType(a.getType())
                 self.dischargeSwap(newAssem, a)