# 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.
"""Unit tests for input modifiers."""
import os
import unittest
from ruamel import yaml
from armi import cases, settings
from armi.cases import suiteBuilder
from armi.cases.inputModifiers import (
inputModifiers,
neutronicsModifiers,
pinTypeInputModifiers,
)
from armi.physics.neutronics.fissionProductModel.fissionProductModelSettings import (
CONF_FP_MODEL,
)
from armi.physics.neutronics.settings import (
CONF_EPS_EIG,
CONF_EPS_FSAVG,
CONF_EPS_FSPOINT,
)
from armi.reactor import blueprints
from armi.reactor.tests import test_reactors
from armi.utils import directoryChangers
FLAGS_INPUT = """nuclide flags:
U: {burn: false, xs: true}
ZR: {burn: false, xs: true}
MN: {burn: false, xs: true}
FE: {burn: false, xs: true}
SI: {burn: false, xs: true}
C: {burn: false, xs: true}
CR: {burn: false, xs: true}
MO: {burn: false, xs: true}
NI: {burn: false, xs: true}
V: {burn: false, xs: true}
W: {burn: false, xs: true}"""
CLAD = """clad: &fuel_1_clad
Tinput: 350.0
Thot: 350.0
shape: circle
id: 1.0
od: 1.1
material: HT9"""
CLAD_LINKED = """clad: &fuel_1_clad
Tinput: 350.0
Thot: 350.0
shape: circle
id: fuel.od
od: 1.1
material: HT9"""
BLOCKS_INPUT = """blocks:
fuel 1: &fuel_1
fuel: &fuel_1_fuel
Tinput: 350.0
Thot: 350.0
shape: circle
id: 0.0
od: 0.5
material: UZr
{clad}
hex: &fuel_1_hex
Tinput: 350.0
Thot: 350.0
shape: hexagon
ip: 1.0
op: 10.0
material: HT9
fuel 2: *fuel_1
block 3: *fuel_1 # non-fuel blocks
block 4: {{<<: *fuel_1}} # non-fuel blocks
block 5: {{fuel: *fuel_1_fuel, clad: *fuel_1_clad, hex: *fuel_1_hex}} # non-fuel blocks"""
BLOCKS_INPUT_1 = BLOCKS_INPUT.format(clad=CLAD)
BLOCKS_INPUT_2 = BLOCKS_INPUT.format(clad=CLAD_LINKED)
BLUEPRINT_INPUT = f"""
{FLAGS_INPUT}
{BLOCKS_INPUT_1}
assemblies: {{}}
"""
BLUEPRINT_INPUT_LINKS = f"""
{FLAGS_INPUT}
{BLOCKS_INPUT_2}
assemblies: {{}}
"""
CORE_INPUT = """
systems:
core:
grid name: core
origin:
x: 0.0
y: 0.0
z: 0.0
grids:
core:
geom: hex
symmetry: third core periodic
grid contents:
[0, 0]: A1
[1, 0]: A2
[1, 1]: A3
[2, -2]: A4
[2, -1]: A5
[2, 0]: A6
[2, 1]: A7
[2, 2]: A8
"""
[docs]
class TestsuiteBuilderIntegrations(unittest.TestCase):
@classmethod
def setUpClass(cls):
bp = blueprints.Blueprints.load(BLUEPRINT_INPUT_LINKS + CORE_INPUT)
cs = settings.Settings()
bp._prepConstruction(cs)
cls.baseCase = cases.Case(cs=cs, bp=bp)
[docs]
def test_smearDensityFail(self):
builder = suiteBuilder.FullFactorialSuiteBuilder(self.baseCase)
builder.addDegreeOfFreedom(pinTypeInputModifiers.SmearDensityModifier(v) for v in (0.5, 0.6))
builder.addDegreeOfFreedom(pinTypeInputModifiers.CladThicknessByIDModifier(v) for v in (0.05, 0.01))
self.assertEqual(4, len(builder))
with self.assertRaisesRegex(RuntimeError, "before .*SmearDensityModifier"):
builder.buildSuite()
[docs]
def test_settingsModifier(self):
builder = suiteBuilder.SeparateEffectsSuiteBuilder(self.baseCase)
builder.addDegreeOfFreedom(
inputModifiers.SettingsModifier(CONF_FP_MODEL, v) for v in ("noFissionProducts", "infinitelyDilute", "MO99")
)
builder.addDegreeOfFreedom(inputModifiers.SettingsModifier("detailedAxialExpansion", v) for v in (True,))
builder.addDegreeOfFreedom(
inputModifiers.SettingsModifier("buGroups", v)
for v in (
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 100],
[3, 5, 7, 9, 10, 20, 100],
[3, 5, 10, 15, 20, 100],
)
)
builder.addDegreeOfFreedom((inputModifiers.FullCoreModifier(),))
with directoryChangers.TemporaryDirectoryChanger():
suite = builder.buildSuite()
for c in suite:
c.writeInputs()
self.assertTrue(os.path.exists("case-suite"))
[docs]
def test_bluePrintBlockModifier(self):
"""Test BluePrintBlockModifier with build suite naming function argument."""
case_nbr = 1
builder = suiteBuilder.FullFactorialSuiteBuilder(self.baseCase)
builder.addDegreeOfFreedom(
[inputModifiers.BluePrintBlockModifier("fuel 1", "clad", "od", float("{:.2f}".format(22 / 7)))]
)
builder.addDegreeOfFreedom([inputModifiers.BluePrintBlockModifier("block 5", "clad", "od", 3.14159)])
def SuiteNaming(index, _case, _mods):
uniquePart = "{:0>4}".format(index + case_nbr)
return os.path.join(
".",
"case-suite-testBPBM",
uniquePart,
self.baseCase.title + "-" + uniquePart,
)
with directoryChangers.TemporaryDirectoryChanger():
suite = builder.buildSuite(namingFunc=SuiteNaming)
suite.writeInputs()
self.assertTrue(os.path.exists("case-suite-testBPBM"))
yamlfile = open(
f"case-suite-testBPBM/000{case_nbr}/armi-000{case_nbr}-blueprints.yaml",
"r",
)
bp_dict = yaml.YAML().load(yamlfile)
yamlfile.close()
self.assertEqual(bp_dict["blocks"]["fuel 1"]["clad"]["od"], 3.14)
self.assertEqual(bp_dict["blocks"]["block 5"]["clad"]["od"], 3.14159)
[docs]
class TestSettingsModifiers(unittest.TestCase):
[docs]
def test_NeutronicConvergenceModifier(self):
cs = settings.Settings()
with self.assertRaises(ValueError):
_ = neutronicsModifiers.NeutronicConvergenceModifier(0.0)
with self.assertRaises(ValueError):
_ = neutronicsModifiers.NeutronicConvergenceModifier(1e-2 + 1e-15)
cs, _ = neutronicsModifiers.NeutronicConvergenceModifier(1e-2)(cs, None)
self.assertAlmostEqual(cs[CONF_EPS_EIG], 1e-2)
self.assertAlmostEqual(cs[CONF_EPS_FSAVG], 1.0)
self.assertAlmostEqual(cs[CONF_EPS_FSPOINT], 1.0)
[docs]
class NeutronicsKernelOpts(inputModifiers.InputModifier):
def __init__(self, neutronicsKernelOpts):
inputModifiers.InputModifier.__init__(self)
self.neutronicsKernelOpts = neutronicsKernelOpts
def __call__(self, cs, bp):
cs = cs.modified(self.neutronicsKernelOpts)
return cs, bp
[docs]
class TestFullCoreModifier(unittest.TestCase):
"""Ensure full core conversion works."""
[docs]
def test_fullCoreConversion(self):
cs = settings.Settings(os.path.join(test_reactors.TEST_ROOT, "armiRun.yaml"))
case = cases.Case(cs=cs)
mod = inputModifiers.FullCoreModifier()
self.assertEqual(case.bp.gridDesigns["core"].symmetry, "third periodic")
case, case.bp = mod(case, case.bp)
self.assertEqual(case.bp.gridDesigns["core"].symmetry, "full")