# Copyright 2019 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.
"""
The neutronics physics package in the ARMI framework.
Neutronics encompasses the modeling of nuclear chain reactions and their associated transmutation and decay.
The ARMI Framework comes with a neutronics plugin that introduces two
independent interfaces:
:py:mod:`~armi.physics.neutronics.fissionProductModel`
Handles fission product modeling
:py:mod:`~armi.physics.neutronics.crossSectionGroupManager`
Handles the management of different cross section "groups"
.. warning:: There is also some legacy and question-raising code in this module that
is here temporarily while we finish untangling some of the neutronics
plugins outside of ARMI.
"""
from enum import IntEnum
import numpy
import tabulate
from armi import plugins
from armi import runLog
from armi.physics.neutronics.const import CONF_CROSS_SECTION
[docs]class NeutronicsPlugin(plugins.ArmiPlugin):
"""
The built-in neutronics plugin with a few capabilities and a lot of state parameter definitions.
"""
[docs] @staticmethod
@plugins.HOOKIMPL
def exposeInterfaces(cs):
"""
Collect and expose all of the interfaces that live under the built-in neutronics package
"""
from armi.physics.neutronics import crossSectionGroupManager
from armi.physics.neutronics.fissionProductModel import fissionProductModel
interfaceInfo = []
for mod in (crossSectionGroupManager, fissionProductModel):
interfaceInfo += plugins.collectInterfaceDescriptions(mod, cs)
return interfaceInfo
[docs] @staticmethod
@plugins.HOOKIMPL
def defineParameters():
from . import parameters as neutronicsParameters
return neutronicsParameters.getNeutronicsParameterDefinitions()
[docs] @staticmethod
@plugins.HOOKIMPL
def defineEntryPoints():
from armi.physics.neutronics import diffIsotxs
entryPoints = [diffIsotxs.CompareIsotxsLibraries]
return entryPoints
[docs] @staticmethod
@plugins.HOOKIMPL
def defineSettings():
from armi.physics.neutronics import settings as neutronicsSettings
from armi.physics.neutronics import crossSectionSettings
from armi.physics.neutronics.fissionProductModel import (
fissionProductModelSettings,
)
settings = [
crossSectionSettings.XSSettingDef(
CONF_CROSS_SECTION,
)
]
settings += neutronicsSettings.defineSettings()
settings += fissionProductModelSettings.defineSettings()
return settings
[docs] @staticmethod
@plugins.HOOKIMPL
def defineSettingsValidators(inspector):
"""Implementation of settings inspections for neutronics settings."""
from armi.physics.neutronics.settings import getNeutronicsSettingValidators
from armi.physics.neutronics.fissionProductModel.fissionProductModelSettings import (
getFissionProductModelSettingValidators,
)
settingsValidators = getNeutronicsSettingValidators(inspector)
settingsValidators.extend(getFissionProductModelSettingValidators(inspector))
return settingsValidators
[docs] @staticmethod
@plugins.HOOKIMPL
def onProcessCoreLoading(core, cs, dbLoad):
applyEffectiveDelayedNeutronFractionToCore(core, cs)
[docs] @staticmethod
@plugins.HOOKIMPL
def getReportContents(r, cs, report, stage, blueprint):
"""Generates the Report Content for the Neutronics Report"""
from armi.physics.neutronics import reports
return reports.insertNeutronicsReport(r, cs, report, stage)
from armi.physics.neutronics.const import (
ALL,
FLUXFILES,
GAMMA,
INPUTOUTPUT,
NEUTRON,
NEUTRONGAMMA,
RESTARTFILES,
)
# ARC and CCCC cross section file format names
COMPXS = "COMPXS"
PMATRX = "PMATRX"
GAMISO = "GAMISO"
PMATRX_EXT = "pmatrx"
GAMISO_EXT = "gamiso"
ISOTXS = "ISOTXS"
# Constants for neutronics calculation types
ADJOINT_CALC = "adjoint"
REAL_CALC = "real"
ADJREAL_CALC = "both"
# Constants for boundary conditions
# All external boundary conditions are set to zero outward current
INFINITE = "Infinite"
# "Planar" external boundaries conditions are set to zero outward current
REFLECTIVE = "Reflective"
# Generalized boundary conditions D * PHI PRIME + A * PHI = 0 where A is user-specified constant,
# D is the diffusion coefficient, PHI PRIME and PHI are the outward current and flux at the
# external boundaries.
GENERAL_BC = "Generalized"
# The following boundary conditions are three approximations of the vacuum boundary condition
# in diffusion theory.
# 'Extrapolated': sets A to 0.4692 (in generalized BC) to have the flux vanishing at
# 0.7104*transport mean free path through linear extrapolation. Derived for plane
# geometries - should be valid for complex geometries unless radius of curvature is
# comparable to the mean free path.
# 'ZeroSurfaceFlux': flux vanishes at the external boundary.
# 'ZeroInwardCurrent': set A to 0.5 (in generalized BC) to have Jminus = 0 at the external boundaries.
EXTRAPOLATED = "Extrapolated"
ZEROFLUX = "ZeroSurfaceFlux"
ZERO_INWARD_CURRENT = "ZeroInwardCurrent"
# Common settings checks
[docs]def gammaTransportIsRequested(cs):
"""
Check if gamma transport was requested by the user.
Arguments
---------
cs : ARMI settings object
Object containing the default and user-specified ARMI settings controlling the simulation
Returns
-------
flag : bool
Returns true if gamma transport is requested.
"""
from armi.physics.neutronics.settings import CONF_GLOBAL_FLUX_ACTIVE
return GAMMA in cs[CONF_GLOBAL_FLUX_ACTIVE]
[docs]def gammaXsAreRequested(cs):
"""
Check if gamma cross-sections generation was requested by the user.
Arguments
---------
cs : ARMI settings object
Object containing the default and user-specified ARMI settings controlling the simulation.
Returns
-------
flag : bool
Returns true if gamma cross section generation is requested.
"""
from armi.physics.neutronics.settings import CONF_GEN_XS
return GAMMA in cs[CONF_GEN_XS]
[docs]def adjointCalculationRequested(cs):
"""Return true if an adjoint calculation is requested based on the ``CONF_NEUTRONICS_TYPE`` setting."""
from armi.physics.neutronics.settings import CONF_NEUTRONICS_TYPE
return cs[CONF_NEUTRONICS_TYPE] in [ADJOINT_CALC, ADJREAL_CALC]
[docs]def realCalculationRequested(cs):
"""Return true if a real calculation is requested based on the ``CONF_NEUTRONICS_TYPE`` type setting."""
from armi.physics.neutronics.settings import CONF_NEUTRONICS_TYPE
return cs[CONF_NEUTRONICS_TYPE] in ["real", "both"]
[docs]def applyEffectiveDelayedNeutronFractionToCore(core, cs):
"""Process the settings for the delayed neutron fraction and precursor decay constants."""
# Verify and set the core beta parameters based on the user-supplied settings
beta = cs["beta"]
decayConstants = cs["decayConstants"]
# If beta is interpreted as a float, then assign it to
# the total delayed neutron fraction parameter. Otherwise, setup the
# group-wise delayed neutron fractions and precursor decay constants.
reportTableData = []
if isinstance(beta, float):
core.p.beta = beta
reportTableData.append(("Total Delayed Neutron Fraction", core.p.beta))
elif isinstance(beta, list) and isinstance(decayConstants, list):
if len(beta) != len(decayConstants):
raise ValueError(
f"The values for `beta` ({beta}) and `decayConstants` "
f"({decayConstants}) are not consistent lengths."
)
core.p.beta = sum(beta)
core.p.betaComponents = numpy.array(beta)
core.p.betaDecayConstants = numpy.array(decayConstants)
reportTableData.append(("Total Delayed Neutron Fraction", core.p.beta))
reportTableData.append(
("Group-wise Delayed Neutron Fractions", core.p.betaComponents)
)
reportTableData.append(
("Group-wise Precursor Decay Constants", core.p.betaDecayConstants)
)
# Report to the user the values were not applied.
if not reportTableData and (beta is not None or decayConstants is not None):
runLog.warning(
f"Delayed neutron fraction(s) - {beta} and decay constants"
" - {decayConstants} have not been applied."
)
else:
runLog.extra(
tabulate.tabulate(
tabular_data=reportTableData,
headers=["Component", "Value"],
tablefmt="armi",
)
)
[docs]class LatticePhysicsFrequency(IntEnum):
"""
Enumeration for lattice physics update frequency options.
NEVER = never automatically trigger lattice physics (a custom script could still trigger it)
BOL = Beginning-of-life (c0n0)
BOC = Beginning-of-cycle (c*n0)
everyNode = Every interaction node (c*n*)
firstCoupledIteration = every node + the first coupled iteration at each node
all = every node + every coupled iteration
Notes
-----
firstCoupledIteration only updates the cross sections during the first coupled iteration,
but not on any subsequent iterations. This may be an appropriate approximation in some cases
to save compute time, but each individual user should give careful consideration to whether
this is the behavior they want for a particular application. The main purpose of this setting
is to capture a large change in temperature distribution when running a snapshot at a different
power/flow condition than the original state being loaded from the database.
"""
never = 0
BOL = 1
BOC = 2
everyNode = 3
firstCoupledIteration = 4
all = 5