Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix numerical diffusion in uniformMesh converter #992

Merged
merged 43 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9cdced2
Fix numerical diffusion in uniformMesh converter
mgjarrett Nov 22, 2022
eb1c806
Black formatting.
mgjarrett Nov 22, 2022
68f1ea4
Map power to uniform mesh.
mgjarrett Nov 22, 2022
a0b195f
Black formatting.
mgjarrett Nov 22, 2022
a68fd65
Extend uniform mesh converter.
mgjarrett Nov 23, 2022
1cb7f98
Use self.photons attribute instead of gamma.
mgjarrett Nov 28, 2022
2821e47
Black formatting.
mgjarrett Nov 28, 2022
f4dec91
Merge branch 'main' into fixUniformMeshParamMapping
mgjarrett Nov 29, 2022
c71a854
Remove outdated function arguments.
mgjarrett Nov 29, 2022
50bad4a
Update unit tests for uniformMesh.
mgjarrett Nov 29, 2022
356a595
More test updates.
mgjarrett Nov 29, 2022
1f8515a
Black formatting.
mgjarrett Nov 29, 2022
588a838
Modify gamma uniform mesh mapping.
mgjarrett Nov 29, 2022
288bad5
Merge remote-tracking branch 'origin/main' into fixUniformMeshParamMa…
mgjarrett Nov 29, 2022
71763a5
Moving hyper-specific TH parameters out of ARMI
john-science Nov 29, 2022
392c152
Use parameter names
mgjarrett Nov 29, 2022
4d36f9a
Remove flux reconstruction parameters.
mgjarrett Nov 29, 2022
ca5ca00
Remove unused applyCachedParamValues.
mgjarrett Nov 30, 2022
7718e39
Uncomment one more test line.
mgjarrett Nov 30, 2022
d009bac
black formatting.
mgjarrett Nov 30, 2022
42e7e5d
Increase test coverage with nonUniformAssems.
mgjarrett Nov 30, 2022
5a9286d
Clean up commented code.
mgjarrett Nov 30, 2022
93046d6
Change default mapNumberDensities back to True
mgjarrett Dec 1, 2022
a2a5fa4
Remove unused imports.
mgjarrett Dec 1, 2022
dc43140
Revert changed mapNumberDensities option
mgjarrett Dec 1, 2022
29cb7a1
Go back to better parameter description.
mgjarrett Dec 1, 2022
7cf32f5
Explain when neutron parameters in gamma category
mgjarrett Dec 2, 2022
3214f0d
Modify parameters to map for neutronics and gamma.
mgjarrett Dec 2, 2022
50c9873
Set default blockParamNames=None
mgjarrett Dec 3, 2022
f6dde5e
Move DPA increment to updateFluenceAndDpa
mgjarrett Dec 2, 2022
71d69c3
Multiply rate by step time.
mgjarrett Dec 5, 2022
9d1df8d
Revert "Multiply rate by step time."
mgjarrett Dec 5, 2022
156a5c6
Revert "Move DPA increment to updateFluenceAndDpa"
mgjarrett Dec 5, 2022
32c0676
Update release notes.
mgjarrett Dec 12, 2022
84fe8f2
Merge branch 'main' into fixUniformMeshParamMapping
mgjarrett Dec 12, 2022
f3f3174
Remove stray print.
mgjarrett Dec 13, 2022
39416d6
Map multiGroupQuantities when performing gamma.
mgjarrett Dec 13, 2022
9479bcc
Update uniform mesh docstrings.
mgjarrett Dec 13, 2022
486f9f6
Add docstring for parameter Category class
mgjarrett Dec 13, 2022
697e7a7
Add warning to docstring
mgjarrett Dec 13, 2022
126642f
Rename BLOCK_PARAMS_TO_MAP
mgjarrett Dec 13, 2022
22da024
Update parameters out for gamma UMC
mgjarrett Dec 14, 2022
f589791
Set default gammaSrc to 0.0
mgjarrett Dec 14, 2022
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
6 changes: 3 additions & 3 deletions armi/physics/neutronics/globalFlux/globalFluxInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
RX_ABS_MICRO_LABELS = ["nGamma", "fission", "nalph", "np", "nd", "nt"]
RX_PARAM_NAMES = ["rateCap", "rateFis", "rateProdN2n", "rateProdFis", "rateAbs"]


# pylint: disable=too-many-public-methods
class GlobalFluxInterface(interfaces.Interface):
"""
Expand Down Expand Up @@ -286,7 +285,7 @@ def __init__(self, label: Optional[str] = None):
self.real = True
self.adjoint = False
self.neutrons = True
self.photons = None
self.photons = False
self.boundaryConditions = {}
self.epsFissionSourceAvg = None
self.epsFissionSourcePoint = None
Expand Down Expand Up @@ -422,7 +421,8 @@ def _performGeometryTransformations(self, makePlots=False):
converter = self.geomConverters.get("axial")
if not converter:
if self.options.detailedAxialExpansion or self.options.hasNonUniformAssems:
converter = uniformMesh.NeutronicsUniformMeshConverter(
converterCls = uniformMesh.converterFactory(self.options)
converter = converterCls(
cs=self.options.cs,
calcReactionRates=self.options.calcReactionRatesOnMeshConversion,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ def getExecuterCls(self):
return MockGlobalFluxExecuter


class MockGlobalFluxWithExecutersNonUniform(MockGlobalFluxWithExecuters):
def getExecuterOptions(self, label=None):
"""
Return modified executerOptions
"""
opts = globalFluxInterface.GlobalFluxInterfaceUsingExecuters.getExecuterOptions(
self, label=label
)
opts.hasNonUniformAssems = True # to increase test coverage
return opts


class MockGlobalFluxExecuter(globalFluxInterface.GlobalFluxExecuter):
"""Tests for code that uses Executers, which rely on OutputReaders to update state."""

Expand Down Expand Up @@ -180,6 +192,33 @@ def test_getExecuterCls(self):
self.assertEqual(class0, globalFluxInterface.GlobalFluxExecuter)


class TestGlobalFluxInterfaceWithExecutersNonUniform(unittest.TestCase):
"""Tests for global flux execution with non-uniform assemblies."""

@classmethod
def setUpClass(cls):
cs = settings.Settings()
_o, cls.r = test_reactors.loadTestReactor()
cls.r.core.p.keff = 1.0
cls.gfi = MockGlobalFluxWithExecutersNonUniform(cls.r, cs)

def test_executerInteractionNonUniformAssems(self):
gfi, r = self.gfi, self.r
gfi.interactBOC()
gfi.interactEveryNode(0, 0)
r.p.timeNode += 1
gfi.interactEveryNode(0, 1)
gfi.interactEOC()
self.assertAlmostEqual(r.core.p.rxSwing, (1.02 - 1.01) / 1.01 * 1e5)

def test_calculateKeff(self):
self.assertEqual(self.gfi.calculateKeff(), 1.05) # set in mock

def test_getExecuterCls(self):
class0 = globalFluxInterface.GlobalFluxInterfaceUsingExecuters.getExecuterCls()
self.assertEqual(class0, globalFluxInterface.GlobalFluxExecuter)


class TestGlobalFluxResultMapper(unittest.TestCase):
"""
Test that global flux result mappings run.
Expand Down
42 changes: 35 additions & 7 deletions armi/physics/neutronics/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def mgFlux(self, value):
categories=[
parameters.Category.fluxQuantities,
parameters.Category.multiGroupQuantities,
parameters.Category.gamma,
],
default=None,
)
Expand Down Expand Up @@ -129,7 +130,10 @@ def mgFlux(self, value):
description="multigroup gamma source",
location=ParamLocation.AVERAGE,
saveToDB=True,
categories=[parameters.Category.multiGroupQuantities],
categories=[
parameters.Category.multiGroupQuantities,
parameters.Category.gamma,
],
default=None,
)

Expand All @@ -139,6 +143,7 @@ def mgFlux(self, value):
description="gamma source",
location=ParamLocation.AVERAGE,
saveToDB=True,
categories=[parameters.Category.gamma],
default=None,
)

Expand Down Expand Up @@ -183,7 +188,7 @@ def mgFlux(self, value):
"pinMgFluxesGamma",
units="g/s/cm$^2$",
description="should be a blank 3-D array, but re-defined later (ng x nPins x nAxialSegments)",
categories=[parameters.Category.pinQuantities],
categories=[parameters.Category.pinQuantities, parameters.Category.gamma],
saveToDB=False,
default=None,
)
Expand Down Expand Up @@ -289,13 +294,14 @@ def linPowByPinNeutron(self, value):
else:
self._p_linPowByPinNeutron = numpy.array(value)

# gamma category because linPowByPin is only split by neutron/gamma when gamma is activated
pb.defParam(
"linPowByPinNeutron",
setter=linPowByPinNeutron,
units="W/cm",
description="Pin linear neutron heat rate. This is the neutron heating component of `linPowByPin`",
location=ParamLocation.CHILDREN,
categories=[parameters.Category.pinQuantities],
categories=[parameters.Category.pinQuantities, parameters.Category.gamma],
default=None,
)

Expand All @@ -311,7 +317,7 @@ def linPowByPinGamma(self, value):
units="W/cm",
description="Pin linear gamma heat rate. This is the gamma heating component of `linPowByPin`",
location=ParamLocation.CHILDREN,
categories=[parameters.Category.pinQuantities],
categories=[parameters.Category.pinQuantities, parameters.Category.gamma],
default=None,
)

Expand All @@ -320,6 +326,7 @@ def linPowByPinGamma(self, value):
units="#/s",
description='List of reaction rates in specified by setting "reactionsToDB"',
location=ParamLocation.VOLUME_INTEGRATED,
categories=[parameters.Category.fluxQuantities],
default=None,
)

Expand Down Expand Up @@ -546,17 +553,25 @@ def linPowByPinGamma(self, value):
"pdensGamma",
units="W/cm^3",
description="Average volumetric gamma power density",
categories=[parameters.Category.gamma],
)

# gamma category because pdens is only split by neutron/gamma when gamma is activated
pb.defParam(
"pdensNeutron",
units="W/cm^3",
description="Average volumetric neutron power density",
categories=[parameters.Category.gamma],
)

pb.defParam("ppdens", units="W/cm^3", description="Peak power density")

pb.defParam("ppdensGamma", units="W/cm^3", description="Peak gamma density")
pb.defParam(
"ppdensGamma",
units="W/cm^3",
description="Peak gamma density",
categories=[parameters.Category.gamma],
)

# rx rate params that are derived during mesh conversion.
# We'd like all things that can be derived from flux and XS to be
Expand Down Expand Up @@ -603,15 +618,27 @@ def linPowByPinGamma(self, value):
"powerGenerated",
units=" W",
description="Generated power. Different than b.p.power only when gamma transport is activated.",
categories=[parameters.Category.gamma],
)

pb.defParam("power", units="W", description="Total power")

pb.defParam("powerDecay", units="W", description="Total decay power")

pb.defParam("powerGamma", units="W", description="Total gamma power")
pb.defParam(
"powerGamma",
units="W",
description="Total gamma power",
categories=[parameters.Category.gamma],
)

pb.defParam("powerNeutron", units="W", description="Total neutron power")
# gamma category because power is only split by neutron/gamma when gamma is activated
pb.defParam(
"powerNeutron",
units="W",
description="Total neutron power",
categories=[parameters.Category.gamma],
)

with pDefs.createBuilder(default=0.0) as pb:
pb.defParam(
Expand Down Expand Up @@ -663,6 +690,7 @@ def linPowByPinGamma(self, value):
units="W/cm^3",
description="Volume-averaged generated power density. Different than b.p.pdens only when gamma transport is activated.",
location=ParamLocation.AVERAGE,
categories=[parameters.Category.gamma],
)

return pDefs
Expand Down
131 changes: 129 additions & 2 deletions armi/reactor/converters/tests/test_uniformMesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@
from armi.tests import TEST_ROOT, ISOAA_PATH


class DummyFluxOptions:
def __init__(self):
self.photons = False


class TestConverterFactory(unittest.TestCase):
def setUp(self):
self.o, self.r = test_reactors.loadTestReactor(
inputFilePath=os.path.join(TEST_ROOT, "detailedAxialExpansion"),
)
self.dummyOptions = DummyFluxOptions()

def test_converterFactory(self):
print(self.dummyOptions)
mgjarrett marked this conversation as resolved.
Show resolved Hide resolved
self.dummyOptions.photons = False
neutronConverter = uniformMesh.converterFactory(self.dummyOptions)
self.assertTrue(neutronConverter, uniformMesh.NeutronicsUniformMeshConverter)

self.dummyOptions.photons = True
gammaConverter = uniformMesh.converterFactory(self.dummyOptions)
self.assertTrue(gammaConverter, uniformMesh.GammaUniformMeshConverter)


class TestAssemblyUniformMesh(unittest.TestCase):
"""
Tests individual operations of the uniform mesh converter
Expand All @@ -48,7 +71,10 @@ def test_makeAssemWithUniformMesh(self):

self.converter._computeAverageAxialMesh()
newAssem = self.converter.makeAssemWithUniformMesh(
sourceAssem, self.converter._uniformMesh
sourceAssem,
self.converter._uniformMesh,
blockParamNames=["power"],
mapNumberDensities=True,
)

prevB = None
Expand Down Expand Up @@ -89,7 +115,7 @@ def test_makeAssemWithUniformMeshSubmesh(self):

self.r.core.updateAxialMesh()
newAssem = self.converter.makeAssemWithUniformMesh(
sourceAssem, self.r.core.p.axialMesh[1:]
sourceAssem, self.r.core.p.axialMesh[1:], blockParamNames=["power"]
)

self.assertNotEqual(len(newAssem), len(sourceAssem))
Expand All @@ -114,6 +140,7 @@ def test_makeAssemUniformMeshParamMappingSameMesh(self):
sourceAssem,
sourceAssem.getAxialMesh(),
blockParamNames=["flux", "power", "mgFlux"],
mapNumberDensities=True,
)
for b, origB in zip(newAssem, sourceAssem):
self.assertEqual(b.p.flux, 1.0)
Expand Down Expand Up @@ -329,6 +356,106 @@ def test_applyStateToOriginal(self):
)


class TestGammaUniformMesh(unittest.TestCase):
"""
Tests gamma uniform mesh converter

Loads reactor once per test
"""

@classmethod
def setUpClass(cls):
# random seed to support random mesh in unit tests below
random.seed(987324987234)

def setUp(self):
self.o, self.r = test_reactors.loadTestReactor(
TEST_ROOT, customSettings={"xsKernel": "MC2v2"}
)
self.r.core.lib = isotxs.readBinary(ISOAA_PATH)
self.r.core.p.keff = 1.0
self.converter = uniformMesh.GammaUniformMeshConverter(
cs=self.o.cs, calcReactionRates=False
)

def test_convertNumberDensities(self):
refMass = self.r.core.getMass("U235")
applyNonUniformHeightDistribution(
self.r
) # this changes the mass of everything in the core
perturbedCoreMass = self.r.core.getMass("U235")
self.assertNotEqual(refMass, perturbedCoreMass)
self.converter.convert(self.r)

uniformReactor = self.converter.convReactor
uniformMass = uniformReactor.core.getMass("U235")

self.assertAlmostEqual(
perturbedCoreMass, uniformMass
) # conversion conserved mass
self.assertAlmostEqual(
self.r.core.getMass("U235"), perturbedCoreMass
) # conversion didn't change source reactor mass

def test_applyStateToOriginal(self):
applyNonUniformHeightDistribution(self.r) # note: this perturbs the ref. mass

# set original parameters on pre-mapped core with non-uniform assemblies
for b in self.r.core.getBlocks():
b.p.mgFlux = range(33)
b.p.adjMgFlux = range(33)
b.p.fastFlux = 2.0
b.p.flux = 5.0
b.p.power = 5.0

# set original parameters on pre-mapped core with non-uniform assemblies
self.converter.convert(self.r)
for b in self.converter.convReactor.core.getBlocks():
b.p.powerGamma = 0.5
b.p.powerNeutron = 0.5

# check integral and density params
assemblyPowers = [
a.calcTotalParam("power") for a in self.converter.convReactor.core
]
assemblyGammaPowers = [
a.calcTotalParam("powerGamma") for a in self.converter.convReactor.core
]
totalPower = self.converter.convReactor.core.calcTotalParam(
"power", generationNum=2
)
totalPowerGamma = self.converter.convReactor.core.calcTotalParam(
"powerGamma", generationNum=2
)

self.converter.applyStateToOriginal()

for b in self.r.core.getBlocks():
# equal to original value because these were never mapped
self.assertEqual(b.p.fastFlux, 2.0)
self.assertEqual(b.p.flux, 5.0)
self.assertEqual(b.p.fastFlux, 2.0)
self.assertEqual(b.p.power, 5.0)

# not equal because blocks are different size
self.assertNotEqual(b.p.powerGamma, 0.5)
self.assertNotEqual(b.p.powerNeutron, 0.5)

# equal because these are mapped
for expectedPower, expectedGammaPower, a in zip(
assemblyPowers, assemblyGammaPowers, self.r.core
):
self.assertAlmostEqual(a.calcTotalParam("power"), expectedPower)
self.assertAlmostEqual(a.calcTotalParam("powerGamma"), expectedGammaPower)

self.assertAlmostEqual(
self.r.core.calcTotalParam("powerGamma", generationNum=2), totalPowerGamma
)
self.assertAlmostEqual(
self.r.core.calcTotalParam("power", generationNum=2), totalPower
)


class TestParamConversion(unittest.TestCase):
def setUp(self):
"""
Expand Down
Loading