# Copyright 2021 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.
"""
Define DIF3D-specific ARMI user-configurable settings.
This module implements the :py:meth:`ArmiPlugin.defineSettings()
<armi:armi.plugins.ArmiPlugin.defineSettings()>` and
:py:meth:`ArmiPlugin.defineSettingsValidators()
<armi:armi.plugins.ArmiPlugin.defineSettingsValidators()>` Plugin APIs. Aside from the
settings that control DIF3D's behavior specifically, this provides new options to the
ARMI built-in ``neutronicsKernel`` setting:
    * "DIF3D-Nodal": Enable DIF3D with nodal solver.
    * "DIF3D-FD": Enable DIF3D with finite-difference solver.
    * "VARIANT": Enable DIF3D with VARIANT Pn solver.
"""
import shutil
from armi.physics.neutronics import settings as neutronicsSettings
from armi.settings import setting
from armi.operators import settingsValidation
from armi.physics import neutronics
CONF_ASYMP_EXTRAP_OF_OVER_RELAX_CALC = "asympExtrapOfOverRelaxCalc"
CONF_ASYMP_EXTRAP_OF_NODAL_CALC = "asympExtrapOfNodalCalc"
CONF_LIST_ISOTXS = "listIsotxs"
CONF_NODAL_APPROX_XY = "nodalApproxXY"
CONF_NODAL_APPROX_Z = "nodalApproxZ"
CONF_COARSE_MESH_REBALANCE = "coarseMeshRebalance"
CONF_D3D_MEM = "d3dMem"
CONF_D3D_MEM2 = "d3dMem2"
CONF_DIF3D_PATH = "dif3dExePath"
CONF_ERF = "erf"
CONF_NEGLECT_FIS = "neglectFis"
CONF_NIP_MEM = "nipMem"
CONF_OUTERS = "outers"
CONF_INNERS = "inners"
CONF_XS_MEM = "xsMem"
CONF_VARIANT_TRANSPORT_AND_SCATTER_ORDER = "variantTransportAndScatterOrder"
CONF_EPS_BURN_TIME = "epsBurnTime"
CONF_EPS_CYCLIC = "epsCyclic"
CONF_EPS_NDENS = "epsNdens"
CONF_NEUTRONICS_OUTPUTS_TO_SAVE = "neutronicsOutputsToSave"
CONF_USE_RADIAL_INNER_ITERATION_ALGORITHM = "useRadialInnerIterationAlgorithm"
CONF_VARIANT_NODAL_SPATIAL_APPROXIMATION = "variantNodalSpatialApproximation"
CONF_DIF3D_DB = "writeDif3dDb"
CONF_OPT_DIF3DNODAL = "DIF3D-Nodal"
CONF_OPT_DIF3DFD = "DIF3D-FD"
CONF_OPT_VARIANT = "VARIANT"
# This should live in the gamma plugin, but the DIF3D plugin is still using it to do
# something with input file declarations. Should refactor and migrate this.
CONF_OPT_GAMSOR = "GAMSOR"
KERNELS = {CONF_OPT_DIF3DNODAL, CONF_OPT_DIF3DFD, CONF_OPT_VARIANT}
[docs]def defineSettings():
    settings = [
        setting.Setting(
            CONF_DIF3D_DB,
            default=False,
            label="Output database with DIF3D neutronics mesh",
            description="If enabled, a database will be created containing the results "
            "of the most recent DIF3D invocation before converting back to the input "
            "mesh. This is useful for visualizing/analyzing the direct results of the "
            "DIF3D run without any mesh conversions taking place.",
        ),
        setting.Option(CONF_OPT_DIF3DNODAL, neutronicsSettings.CONF_NEUTRONICS_KERNEL),
        setting.Option(CONF_OPT_DIF3DFD, neutronicsSettings.CONF_NEUTRONICS_KERNEL),
        setting.Option(CONF_OPT_VARIANT, neutronicsSettings.CONF_NEUTRONICS_KERNEL),
        setting.Setting(
            CONF_ASYMP_EXTRAP_OF_OVER_RELAX_CALC,
            default=0,
            label="Acceleration of optimum overrelaxation factor calculation",
            description=(
                "Asymptotic source extrapolation of power iterations used to estimate "
                "the spectral radius of each within group iteration matrix. Intended "
                "for problems with overrelaxation factor > 1.8."
            ),
        ),
        setting.Setting(
            CONF_ASYMP_EXTRAP_OF_NODAL_CALC,
            default=0,
            label="Perform asymptotic source extrapolation.",
            description=(
                "Perform asymptotic source extrapolation on the the nodal outer "
                "iterations. Applies to DIF3D-Nodal and VARIANT."
            ),
        ),
        setting.Setting(
            CONF_LIST_ISOTXS,
            default=False,
            label="listIsotxs",
            description="list ISOTXS in the DIF3D output file",
        ),
        setting.Setting(
            CONF_NODAL_APPROX_XY,
            default=40,
            label="XY Nodal approx",
            description=(
                "Approximation controls in XY-Plane (LMN). L can either be 0 (diffusion) "
                "or 1 (transport), M is the flux approximation order (2: quadratic, "
                "3: cubic, or 4: quartic), and N is the leakage approximation order "
                "(0: constant or 2: quadratic). For details, see A.DIF3D file formats "
                "document, under TYPE 10."
            ),
        ),
        setting.Setting(
            CONF_NODAL_APPROX_Z,
            default=32,
            label="Z Nodal approx",
            description=(
                "Approximation controls in Z-direction. M is the flux approximation "
                "order (2: quadratic, 3: cubic, or 4: quartic), and N is the leakage approximation "
                "order (0: constant or 2: quadratic). For details, see A.DIF3D file formats"
                " document, under TYPE 10."
            ),
        ),
        setting.Setting(
            CONF_COARSE_MESH_REBALANCE,
            default=0,
            label="Coarse mesh rebalance",
            description=(
                "Sets the coarse-mesh rebalance acceleration to something other than the "
                "default."
            ),
        ),
        setting.Setting(
            CONF_D3D_MEM,
            default=24000000,
            label="Extended Core Memory Size",
            description=(
                "POINTR container array size in extended core memory for A.DIF3D card of "
                "DIF3D/REBUS. Max recommended=159999000"
            ),
        ),
        setting.Setting(
            CONF_D3D_MEM2,
            default=40000000,
            label="Fast Core Memory Size",
            description=(
                "POINTR container array size in fast core memory in REAL*8 words "
                "in A.DIF3D card of DIF3D/REBUS. Max recommended=40000000"
            ),
        ),
        setting.Setting(
            CONF_DIF3D_PATH,
            default="dif3d",
            label="DIF3D path",
            description="The path do the DIF3D executable",
            options=[],
        ),
        setting.Setting(
            CONF_ERF,
            default=0.04,
            label="Inner iteration error reduction factor",
            description=(
                "Error reduction factor to be achieved by each series of inner "
                "iteration for each group during a shape calculation in DIF3D/REBUS. "
                "Reduce to 0.01 if dominance ratio estimate is sporadic, or if pointwise "
                "fission source convergence is not monotonic."
            ),
        ),
        setting.Setting(
            CONF_NEGLECT_FIS,
            default=0.001,
            label="Min. fission source",
            description=(
                "Any pointwise fission source will be neglected in the pointwise "
                "fission source convergence test if it is less than this factor "
                "times the RMS fission source in DIF3D/REBUS"
            ),
        ),
        setting.Setting(
            CONF_NIP_MEM,
            default=40000000,
            label="Memory for A.NIP3",
            description=(
                "Size of main core storage array for geometry processing module "
                "(GNIP4C) in A.NIP card of DIF3D/REBUS. Max recommended=40000000"
            ),
        ),
        setting.Setting(
            CONF_OUTERS,
            default=100,
            label="Max Outer Iterations",
            description="Max number of outer iterations to converge",
        ),
        setting.Setting(
            CONF_INNERS,
            default=0,
            label="Inner Iterations",
            description=(
                "XY and Axial partial current sweep inner iterations. 0 is let DIF3D "
                "pick or use default if can't pick."
            ),
        ),
        setting.Setting(
            CONF_XS_MEM,
            default=40000000,
            label="XS Processing Memory Size",
            description=(
                "Size of main core storage array for cross section processing modules. "
                "Max recommended=40000000"
            ),
        ),
        setting.Setting(
            CONF_VARIANT_TRANSPORT_AND_SCATTER_ORDER,
            default="",
            label="VARIANT Flux/Leakage Angle and Scattering Orders",
            description=(
                "The flux/leakage angle and scattering orders to use with neutronics "
                "kernel VARIANT."
            ),
            options=["", "P1P0", "P3P1", "P3P3", "P5P1", "P5P3"],
        ),
        setting.Setting(
            CONF_EPS_BURN_TIME,
            default=1.0,
            label="Burn time eps",
            description=(
                "Burn time eps (Cycle length convergence.  "
                "Set to 1.0 if the cycle length is known.)"
            ),
        ),
        setting.Setting(
            CONF_EPS_CYCLIC,
            default=0.001,
            label="Cyclic density eps",
            description="max relative error in isotope stage density during cyclics  (0.001)",
        ),
        setting.Setting(
            CONF_EPS_NDENS,
            default=0.001,
            label="Region Ndens eps",
            description="max relative error in any isotope in region density (0.001)",
        ),
        setting.Setting(
            CONF_NEUTRONICS_OUTPUTS_TO_SAVE,
            default="Input/Output",
            label="Save DIF3D Files",
            description=(
                "Defines outputs from DIF3D-based neutronics kernel to be copied from "
                "the fast path to the network drive for inspection, restarts, debugging, "
                "etc."
            ),
            options=["", "Input/Output", "Flux files", "Restart files", "All"],
        ),
        setting.Setting(
            CONF_USE_RADIAL_INNER_ITERATION_ALGORITHM,
            default=False,
            label="Use Radial Inner Iter Algorithm",
            description=(
                "Use the VARIANT Radial Inner Iteration Algorithm which is helpful for "
                "cases with small node mesh. Type 12 card in A.DIF3D"
            ),
        ),
        setting.Setting(
            CONF_VARIANT_NODAL_SPATIAL_APPROXIMATION,
            default="20501",  # minimum required for hex
            label="VARIANT Nodal Spatial Approx.",
            description=(
                "The Nodal Spatial polynomial approximation in VARIANT. See Type 12 card "
                "in A.DIF3D for information."
            ),
        ),
    ]
    return settings 
[docs]def defineSettingValidators(inspector):
    """Define DIF3D-related setting validations."""
    queries = [
        settingsValidation.Query(
            lambda: (
                inspector.cs[neutronicsSettings.CONF_NEUTRONICS_KERNEL]
                == CONF_OPT_DIF3DNODAL
                and neutronics.adjointCalculationRequested(inspector.cs)
            )
            and inspector.cs[CONF_COARSE_MESH_REBALANCE] > -1,
            "The DIF3D nodal approximation will not converge for the adjoint flux solution if the "
            f"`{CONF_COARSE_MESH_REBALANCE}` setting is enabled.",
            f"Disable `{CONF_COARSE_MESH_REBALANCE}`?",
            lambda: inspector._assignCS(CONF_COARSE_MESH_REBALANCE, -1),
        ),
        settingsValidation.Query(
            lambda: inspector.cs[neutronicsSettings.CONF_NEUTRONICS_KERNEL]
            == CONF_OPT_DIF3DNODAL
            and inspector.cs[CONF_ASYMP_EXTRAP_OF_NODAL_CALC] == -1,
            f"The value of `{CONF_ASYMP_EXTRAP_OF_NODAL_CALC}` is not valid "
            f"for {CONF_OPT_DIF3DNODAL}",
            "Set value to 0 to perform asymptotic source extrapolation?",
            lambda: inspector._assignCS(CONF_ASYMP_EXTRAP_OF_NODAL_CALC, 0),
        ),
        settingsValidation.Query(
            lambda: inspector.cs[neutronicsSettings.CONF_NEUTRONICS_KERNEL]
            == CONF_OPT_VARIANT
            and not inspector.cs[CONF_VARIANT_TRANSPORT_AND_SCATTER_ORDER],
            f"The value of `{CONF_VARIANT_TRANSPORT_AND_SCATTER_ORDER}` must be set "
            f"for {CONF_OPT_VARIANT}",
            "Set value to P3P3?",
            lambda: inspector._assignCS(
                CONF_VARIANT_TRANSPORT_AND_SCATTER_ORDER, "P3P3"
            ),
        ),
        settingsValidation.Query(
            lambda: shutil.which(
                inspector.cs[CONF_DIF3D_PATH]
            )
            is None,
            "The provided DIF3D executable cannot be found: {}".format(inspector.cs[CONF_DIF3D_PATH]),
            "Please update executable location to the valid location.",
            inspector.NO_ACTION,
        ),
    ]
    return queries