Skip to content

Make axial linking aware of block grids for axial expansion #2145

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

Open
wants to merge 50 commits into
base: mixed-pin-assemblies
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
202be36
init commit for bringing back block grid-based linking for axial exp
albeanth May 19, 2025
5ec6727
add test for AxialExpansionChanger._checkForBlocksWithoutSolids
albeanth May 20, 2025
3f1dfa4
WIP blueprints updates for multipin
albeanth May 21, 2025
4f6d00c
get blueprints to work with unit tests
albeanth May 22, 2025
a2ccc96
need to have axial shield in the multi pin assembly have the block grid
albeanth May 22, 2025
dacdab7
no need for storing upper linkage
albeanth May 22, 2025
9419593
simplify setup and teardown for axial exp unit tests
albeanth May 22, 2025
5fe590a
simplify test_coldAssemblyExpansion and add type hints
albeanth May 22, 2025
5d68b79
simplify TestComponentLinks. test_differentMultNotOverlapping and tes…
albeanth May 22, 2025
fc41851
fix TestAxialLinkHelper to not have upper
albeanth May 22, 2025
2345c3e
improve tests on exceptions with regex
albeanth May 22, 2025
0b4582a
clean up exceptions in expansionData.py
albeanth May 22, 2025
c5fb9ea
replace old str format with f-strings
albeanth May 22, 2025
9ec7e8c
add some type checking
albeanth May 22, 2025
3d438a5
fix failing uniform mesh test
albeanth May 22, 2025
dad410c
first cut at docs updates for new linking
albeanth May 23, 2025
87c993b
misc cleanup + type hints
albeanth May 23, 2025
e1053c1
black and lint
albeanth May 23, 2025
0fd71ed
move assembly linking specific tests to their own test file
albeanth May 23, 2025
6729f43
clean up areAxiallyLinked and add dedicated unit tests for it
albeanth May 23, 2025
50e60ee
add license to new test file
albeanth May 23, 2025
a798262
Merge branch 'main' into newAxialExpLinking
albeanth May 28, 2025
f8d0adb
update method to allow multiple clad within a block
albeanth May 30, 2025
24a4304
Update armi/reactor/converters/axialExpansionChanger/assemblyAxialLin…
albeanth May 30, 2025
6a77014
enable target component inferring to be on a block-by-block basis
albeanth May 30, 2025
a734aa7
Merge branch 'main' into newAxialExpLinking
albeanth May 30, 2025
9e1f3b1
improve runtime error messages for component names and radial links
albeanth May 30, 2025
6939af3
Apply suggestions from code review
albeanth Jun 2, 2025
304aade
improve docstring for _checkOverlap
albeanth Jun 2, 2025
386b8df
make plenum vol calc clearer
albeanth Jun 2, 2025
e40089a
reviewer comments on grid-based axial linkage needing to be exact
albeanth Jun 2, 2025
bc09845
clean up docstrings on setting axial exp target comp
albeanth Jun 3, 2025
0080e09
reviewer feedback on Hexagon use
albeanth Jun 3, 2025
577a88a
improve some type checks
albeanth Jun 3, 2025
1241895
simplify assertions
albeanth Jun 3, 2025
8536793
improve InputError statement
albeanth Jun 3, 2025
11ee728
fix fluid block checker
albeanth Jun 3, 2025
e84c018
fix block-based linking to require exact matches
albeanth Jun 5, 2025
e694041
fix type hints + ruff
albeanth Jun 5, 2025
1925807
mas type hints
albeanth Jun 5, 2025
05999c9
type hints
albeanth Jun 5, 2025
ea6348b
type hints
albeanth Jun 5, 2025
8641f19
Merge branch 'mixed-pin-assemblies' into newAxialExpLinking
albeanth Jun 5, 2025
6ecf7c5
fix merge conflict mess up
albeanth Jun 5, 2025
a27f025
make pin lattice smaller
albeanth Jun 5, 2025
3e6c1d5
making detailedAxialExp core grid real real small
albeanth Jun 5, 2025
f16abe4
ruff it up
albeanth Jun 5, 2025
853552a
make multiple component axial linking error message better
albeanth Jun 5, 2025
460d003
fix union type hints for py3.9
albeanth Jun 6, 2025
843e290
fix union type hint
albeanth Jun 6, 2025
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
25 changes: 16 additions & 9 deletions armi/reactor/assemblies.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from armi import runLog
from armi.materials.material import Fluid
from armi.reactor import assemblyParameters, blocks, composites, grids
from armi.reactor.flags import Flags
from armi.reactor.flags import Flags, TypeSpec
from armi.reactor.parameters import ParamLocation
from armi.reactor.spentFuelPool import SpentFuelPool

Expand Down Expand Up @@ -288,9 +288,9 @@ def getVolume(self):
"""Calculate the total assembly volume in cm^3."""
return self.getArea() * self.getTotalHeight()

def getPinPlenumVolumeInCubicMeters(self):
def getPinPlenumVolumeInCubicMeters(self) -> float:
"""
Return the volume of the plenum for a pin in an assembly.
Return the total volume of the plenum for an assembly in m^3.

Notes
-----
Expand All @@ -299,12 +299,19 @@ def getPinPlenumVolumeInCubicMeters(self):
Warning
-------
This is a bit design-specific for pinned assemblies.

Returns
-------
float: Total plenum volume for an assembly.
"""
plenumVolume = 0.0
for b in self.iterChildrenWithFlags(Flags.PLENUM):
cladId = b.getComponent(Flags.CLAD).getDimension("id")
length = b.getHeight()
plenumVolume += math.pi * (cladId / 2.0) ** 2.0 * length * 1e-6 # convert cm^3 to m^3
for c in b.iterChildrenWithFlags(Flags.CLAD):
cladId = c.getDimension("id")
plenumVolume += math.pi * (cladId / 2.0) ** 2.0 * length
# convert vol from cm^3 to m^3
plenumVolume *= 1e-6
return plenumVolume

def getAveragePlenumTemperature(self):
Expand Down Expand Up @@ -848,14 +855,14 @@ def getBlocksAndZ(self, typeSpec=None, returnBottomZ=False, returnTopZ=False):
def hasContinuousCoolantChannel(self):
return all(b.containsAtLeastOneChildWithFlags(Flags.COOLANT) for b in self)

def getFirstBlock(self, typeSpec=None, exact=False):
def getFirstBlock(self, typeSpec: TypeSpec = None, exact: bool = False) -> Optional[blocks.Block]:
"""Find the first block that matches the spec.

Parameters
----------
typeSpec : flag or list of flags, optional
typeSpec
Specification to require on the returned block.
exact : bool, optional
exact
Require block to exactly match ``typeSpec``

Returns
Expand All @@ -874,7 +881,7 @@ def getFirstBlock(self, typeSpec=None, exact=False):
# No items found in the iteration -> no blocks match the request
return None

def getFirstBlockByType(self, typeName):
def getFirstBlockByType(self, typeName: str) -> Optional[blocks.Block]:
blocks = filter(lambda b: b.getType() == typeName, self)
try:
return next(blocks)
Expand Down
4 changes: 2 additions & 2 deletions armi/reactor/blueprints/blockBlueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ def construct(self, cs, blueprint, axialIndex, axialMeshPoints, height, xsType,
mult = c.getDimension("mult")
if mult and mult != 1.0 and mult != len(c.spatialLocator):
raise ValueError(
f"Conflicting ``mult`` input ({mult}) and number of "
f"lattice positions ({len(c.spatialLocator)}) for {c}. "
f"For {c} in {self.name} there is a conflicting ``mult`` input ({mult}) "
f"and number of lattice positions ({len(c.spatialLocator)}). "
"Recommend leaving off ``mult`` input when using grids."
)
elif not mult or mult == 1.0:
Expand Down
13 changes: 8 additions & 5 deletions armi/reactor/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import copy
import re
from typing import Optional
from typing import Optional, Union

import numpy as np

Expand Down Expand Up @@ -326,9 +326,12 @@ def resolveLinkedDims(self, components):
self.p[dimName] = _DimensionLink((comp, linkedKey))
except Exception:
if value.count(".") > 1:
raise ValueError(f"Component names should not have periods in them: `{value}`")
raise ValueError(
f"Name of {self} has a period in it. "
f"Components cannot not have periods in their names: `{value}`"
)
else:
raise KeyError(f"Bad component link `{dimName}` defined as `{value}`")
raise KeyError(f"Bad component link `{dimName}` defined as `{value}` in {self}")

def setLink(self, key, otherComp, otherCompKey):
"""Set the dimension link."""
Expand Down Expand Up @@ -662,7 +665,7 @@ def getNumberDensity(self, nucName):
"""
return self.p.numberDensities.get(nucName, 0.0)

def getNuclideNumberDensities(self, nucNames):
def getNuclideNumberDensities(self, nucNames: list[str]) -> list[float]:
"""Return a list of number densities for the nuc names requested."""
return [self.p.numberDensities.get(nucName, 0.0) for nucName in nucNames]

Expand Down Expand Up @@ -826,7 +829,7 @@ def getMassEnrichment(self):
except ZeroDivisionError:
return 0.0

def getMass(self, nuclideNames=None):
def getMass(self, nuclideNames: Union[None, str, list[str]] = None) -> float:
r"""
Determine the mass in grams of nuclide(s) and/or elements in this object.

Expand Down
43 changes: 32 additions & 11 deletions armi/reactor/composites.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,17 @@
import itertools
import operator
import timeit
from typing import Callable, Dict, Iterator, List, Optional, Tuple, Type, Union
from typing import (
TYPE_CHECKING,
Callable,
Dict,
Iterator,
List,
Optional,
Tuple,
Type,
Union,
)

import numpy as np

Expand All @@ -51,6 +61,9 @@
from armi.utils.densityTools import calculateNumberDensity
from armi.utils.flags import auto

if TYPE_CHECKING:
from armi.reactor.components.component import Component


class FlagSerializer(parameters.Serializer):
"""
Expand Down Expand Up @@ -873,7 +886,7 @@ def getMaxArea(self):
"""
raise NotImplementedError()

def getMass(self, nuclideNames=None) -> float:
def getMass(self, nuclideNames: Union[None, str, list[str]] = None) -> float:
"""
Determine the mass in grams of nuclide(s) and/or elements in this object.

Expand All @@ -887,7 +900,7 @@ def getMass(self, nuclideNames=None) -> float:

Parameters
----------
nuclideNames : str, optional
nuclideNames
The nuclide/element specifier to get the mass of in the object.
If omitted, total mass is returned.

Expand Down Expand Up @@ -917,7 +930,7 @@ def getMicroSuffix(self):
" of composite such as Blocks or Components have the concept of micro suffixes."
)

def _getNuclidesFromSpecifier(self, nucSpec):
def _getNuclidesFromSpecifier(self, nucSpec: Union[None, str, list[str]]):
"""
Convert a nuclide specification to a list of valid nuclide/element keys.

Expand Down Expand Up @@ -2179,14 +2192,18 @@ def hasComponents(self, typeSpec: Union[TypeSpec, List[TypeSpec]], exact=False):

return all(self.getComponents(t, exact) for t in typeSpec)

def getComponentByName(self, name):
def getComponentByName(self, name: str) -> "Component":
"""
Gets a particular component from this object, based on its name.

Parameters
----------
name : str
name
The blueprint name of the component to return

Returns
-------
Component, c, whose c.name matches name.
"""
components = [c for c in self.iterComponents() if c.name == name]
nComp = len(components)
Expand All @@ -2197,7 +2214,7 @@ def getComponentByName(self, name):
else:
return components[0]

def getComponent(self, typeSpec: TypeSpec, exact=False, quiet=False):
def getComponent(self, typeSpec: TypeSpec, exact: bool = False, quiet: bool = False) -> Optional["Component"]:
"""
Get a particular component from this object.

Expand All @@ -2217,20 +2234,24 @@ def getComponent(self, typeSpec: TypeSpec, exact=False, quiet=False):
Returns
-------
Component : The component that matches the criteria or None

Raises
------
ValueError: more than one Component matches the typeSpec
"""
results = self.getComponents(typeSpec, exact=exact)
if len(results) == 1:
return results[0]
elif not results:
if not quiet:
runLog.warning(
"No component matched {0} in {1}. Returning None".format(typeSpec, self),
f"No component matched {typeSpec} in {self}. Returning None",
single=True,
label="None component returned instead of {0}".format(typeSpec),
label=f"None component returned instead of {typeSpec}",
)
return None
else:
raise ValueError("Multiple components match in {} match typeSpec {}: {}".format(self, typeSpec, results))
raise ValueError(f"Multiple components match in {self} match typeSpec {typeSpec}: {results}")

def getNumComponents(self, typeSpec: TypeSpec, exact=False):
"""
Expand Down Expand Up @@ -2645,7 +2666,7 @@ def getChildren(
def getComponents(self, typeSpec: TypeSpec = None, exact=False):
return list(self.iterComponents(typeSpec, exact))

def iterComponents(self, typeSpec: TypeSpec = None, exact=False):
def iterComponents(self, typeSpec: TypeSpec = None, exact: bool = False) -> Iterator["Component"]:
"""
Return an iterator of armi.reactor.component.Component objects within this Composite.

Expand Down
Loading
Loading