Skip to content

Commit

Permalink
Merge 7fc3860 into b73f595
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuavictorchen committed Oct 12, 2022
2 parents b73f595 + 7fc3860 commit 6b50c33
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 218 deletions.
15 changes: 0 additions & 15 deletions armi/plugins.py
Expand Up @@ -611,21 +611,6 @@ def defineSystemBuilders() -> Dict[str, Callable[[str], "Composite"]]:
and a ``"sfp"`` lookup, triggered to run after all other hooks have been run.
"""

@staticmethod
@HOOKSPEC
def applyZoningStrategy(core, cs) -> int:
"""
Function for adding Zones directly on the Core.
This will take in a Core object, and use any arbitrary logic to create from scratch
or modify Core.zones.
Returns
-------
int
Number of Zones created
"""


class UserPlugin(ArmiPlugin):
"""
Expand Down
5 changes: 4 additions & 1 deletion armi/reactor/converters/geometryConverters.py
Expand Up @@ -1250,14 +1250,17 @@ def convert(self, r=None):
for a in r.core.getAssemblies():
# make extras and add them too. since the input is assumed to be 1/3 core.
otherLocs = grid.getSymmetricEquivalents(a.spatialLocator.indices)
thisZone = r.core.zones.findZoneItIsIn(a) if len(r.core.zones) > 0 else None
angle = 2 * math.pi / (len(otherLocs) + 1)
count = 1
for i, j in otherLocs:
newAssem = copy.deepcopy(a)
newAssem.makeUnique()
newAssem.rotate(count * angle)
count = count + 1
count += 1
r.core.add(newAssem, r.core.spatialGrid[i, j, 0])
if thisZone:
thisZone.addLoc(newAssem.getLocation())
self._newAssembliesAdded.append(newAssem)

if a.getLocation() == "001-001":
Expand Down
54 changes: 43 additions & 11 deletions armi/reactor/reactors.py
Expand Up @@ -201,7 +201,7 @@ def __init__(self, name):
self.locParams = {} # location-based parameters
# overridden in case.py to include pre-reactor time.
self.timeOfStart = time.time()
self.zones = None
self.zones = zones.Zones() # initialize with empty Zones object
# initialize the list that holds all shuffles
self.moveList = {}
self.scalarVals = {}
Expand Down Expand Up @@ -1212,9 +1212,6 @@ def regenAssemblyLists(self):
"""
self._getAssembliesByName()
self._genBlocksByName()
runLog.important("Regenerating Core Zones")
# TODO: this call is questionable... the cs should correspond to analysis
self.buildZones(settings.getMasterCs())
self._genChildByLocationLookupTable()

def getAllXsSuffixes(self):
Expand Down Expand Up @@ -1760,10 +1757,6 @@ def getAssembliesOnSymmetryLine(self, symmetryLineID):
assembliesOnLine.sort(key=lambda a: a.spatialLocator.getRingPos())
return assembliesOnLine

def buildZones(self, cs):
"""Update the zones on the reactor."""
zones.buildZones(self, cs)

def getCoreRadius(self):
"""Returns a radius that the core would fit into."""
return self.getNumRings(indexBased=True) * self.getFirstBlock().getPitch()
Expand Down Expand Up @@ -2307,9 +2300,48 @@ def processLoading(self, cs, dbLoad: bool = False):

self.stationaryBlockFlagsList = stationaryBlockFlags

# Perform initial zoning task
self.buildZones(cs)

self.p.maxAssemNum = self.getMaxParam("assemNum")

getPluginManagerOrFail().hook.onProcessCoreLoading(core=self, cs=cs)

def buildManualZones(self, cs):
"""
Build the Zones that are defined manually in the given CaseSettings file,
in the `zoneDefinitions` setting.
Parameters
----------
cs : CaseSettings
The standard ARMI settings object
Examples
--------
Manual zones will be defined in a special string format, e.g.:
zoneDefinitions:
- ring-1: 001-001
- ring-2: 002-001, 002-002
- ring-3: 003-001, 003-002, 003-003
Notes
-----
This function will just define the Zones it sees in the settings, it does
not do any validation against a Core object to ensure those manual zones
make sense.
"""
runLog.debug(
"Building Zones by manual definitions in `zoneDefinitions` setting"
)
stripper = lambda s: s.strip()
self.zones = zones.Zones()

# parse the special input string for zone definitions
for zoneString in cs["zoneDefinitions"]:
zoneName, zoneLocs = zoneString.split(":")
zoneLocs = zoneLocs.split(",")
zone = zones.Zone(zoneName.strip())
zone.addLocs(map(stripper, zoneLocs))
self.zones.addZone(zone)

if not len(self.zones):
runLog.debug("No manual zones defined in `zoneDefinitions` setting")
26 changes: 26 additions & 0 deletions armi/reactor/tests/test_reactors.py
Expand Up @@ -847,6 +847,32 @@ def test_nonUniformAssems(self):
heights = [b.p.height for b in a]
self.assertEqual(originalHeights, heights)

def test_buildManualZones(self):
# define some manual zones in the settings
newSettings = {}
newSettings["zoneDefinitions"] = [
"ring-1: 001-001",
"ring-2: 002-001, 002-002",
"ring-3: 003-001, 003-002, 003-003",
]
cs = self.o.cs.modified(newSettings=newSettings)
self.r.core.buildManualZones(cs)

zonez = self.r.core.zones
self.assertEqual(len(list(zonez)), 3)
self.assertIn("002-001", zonez["ring-2"])
self.assertIn("003-002", zonez["ring-3"])

def test_buildManualZonesEmpty(self):
# ensure there are no zone definitions in the settings
newSettings = {}
newSettings["zoneDefinitions"] = []
cs = self.o.cs.modified(newSettings=newSettings)

# verify that buildZones behaves well when no zones are defined
self.r.core.buildManualZones(cs)
self.assertEqual(len(list(self.r.core.zones)), 0)


class CartesianReactorTests(ReactorTests):
def setUp(self):
Expand Down
62 changes: 32 additions & 30 deletions armi/reactor/tests/test_zones.py
Expand Up @@ -27,7 +27,6 @@
from armi.reactor import reactors
from armi.reactor import zones
from armi.reactor.tests import test_reactors
from armi.settings.fwSettings import globalSettings
from armi.tests import mockRunLogs

THIS_DIR = os.path.dirname(__file__)
Expand Down Expand Up @@ -177,9 +176,8 @@ def setUp(self):
"ring-3: 003-001, 003-002, 003-003",
]
cs = self.o.cs.modified(newSettings=newSettings)
zones.buildZones(self.r.core, cs)
self.r.core.buildManualZones(cs)
self.zonez = self.r.core.zones
self.r.core.zones = self.zonez

def test_dictionaryInterface(self):
zs = zones.Zones()
Expand Down Expand Up @@ -227,7 +225,7 @@ def test_findZoneItIsIn(self):
]
cs = self.o.cs.modified(newSettings=newSettings)

zones.buildZones(self.r.core, cs)
self.r.core.buildManualZones(cs)
daZones = self.r.core.zones
for zone in daZones:
a = self.r.core.getAssemblyWithStringLocation(sorted(zone.locs)[0])
Expand All @@ -244,6 +242,36 @@ def test_findZoneItIsIn(self):
# ensure that we can no longer find the assembly in the zone
self.assertEqual(daZones.findZoneItIsIn(a), None)

def test_getZoneLocations(self):
# customize settings for this test
newSettings = {}
newSettings["zoneDefinitions"] = [
"ring-1: 001-001",
"ring-2: 002-001, 002-002",
]
cs = self.o.cs.modified(newSettings=newSettings)
self.r.core.buildManualZones(cs)

# test the retrieval of zone locations
self.assertEqual(
set(["002-001", "002-002"]), self.r.core.zones.getZoneLocations("ring-2")
)

def test_getAllLocations(self):
# customize settings for this test
newSettings = {}
newSettings["zoneDefinitions"] = [
"ring-1: 001-001",
"ring-2: 002-001, 002-002",
]
cs = self.o.cs.modified(newSettings=newSettings)
self.r.core.buildManualZones(cs)

# test the retrieval of zone locations
self.assertEqual(
set(["001-001", "002-001", "002-002"]), self.r.core.zones.getAllLocations()
)

def test_summary(self):
# make sure we have a couple of zones to test on
for name0 in ["ring-1", "ring-2", "ring-3"]:
Expand All @@ -263,32 +291,6 @@ def test_summary(self):
self.assertIn("- ring-3: ", mock._outputStream)
self.assertIn("003-001, 003-002, 003-003", mock._outputStream)

def test_buildManualZones(self):
# define some manual zones in the settings
newSettings = {}
newSettings["zoneDefinitions"] = [
"ring-1: 001-001",
"ring-2: 002-001, 002-002",
"ring-3: 003-001, 003-002, 003-003",
]
cs = self.o.cs.modified(newSettings=newSettings)
zones.buildZones(self.r.core, cs)

zonez = self.r.core.zones
self.assertEqual(len(list(zonez)), 3)
self.assertIn("002-001", zonez["ring-2"])
self.assertIn("003-002", zonez["ring-3"])

def test_buildManualZonesEmpty(self):
# ensure there are no zone definitions in the settings
newSettings = {}
newSettings["zoneDefinitions"] = []
cs = self.o.cs.modified(newSettings=newSettings)

# verify that buildZones behaves well when no zones are defined
zones.buildZones(self.r.core, cs)
self.assertEqual(len(list(self.r.core.zones)), 0)

def test_sortZones(self):
# create some zones in non-alphabetical order
zs = zones.Zones()
Expand Down
85 changes: 2 additions & 83 deletions armi/reactor/zones.py
Expand Up @@ -19,7 +19,6 @@
"""
from typing import Iterator, List, Optional, Set, Union

from armi import getPluginManagerOrFail
from armi import runLog
from armi.reactor.assemblies import Assembly
from armi.reactor.blocks import Block
Expand Down Expand Up @@ -368,8 +367,8 @@ def getAllLocations(self) -> Set:
A combination set of all locations, from every Zone
"""
locs = set()
for zoneName in self:
locs.update(self[zoneName])
for zone in self:
locs.update(self[zone.name])

return locs

Expand Down Expand Up @@ -430,83 +429,3 @@ def summary(self) -> None:
locs = sorted(self._zones[name].locs)
line = "- {0}: ".format(name) + ", ".join(locs)
runLog.info(line)


def buildZones(core, cs) -> None:
"""
Build/update the Zones.
The zoning option is determined by the ``zoningStrategy`` setting.
Parameters
----------
core : Core
A fully-initialized Core object
cs : CaseSettings
The standard ARMI settings object
Notes
-----
This method is being reconsidered, so it currently only supports manual zoning.
Returns
-------
None
"""
zoneCounts = getPluginManagerOrFail().hook.applyZoningStrategy(core=core, cs=cs)

if len(zoneCounts) > 1:
raise RuntimeError("Only one plugin can register a Zoning Strategy.")

if len(zoneCounts) == 0:
zones = Zones()
zones.addZones(buildManualZones(cs))
core.zones = zones


def buildManualZones(cs):
"""
Build the Zones that are defined manually in the given CaseSettings file,
in the `zoneDefinitions` setting.
Parameters
----------
cs : CaseSettings
The standard ARMI settings object
Examples
--------
Manual zones will be defined in a special string format, e.g.:
zoneDefinitions:
- ring-1: 001-001
- ring-2: 002-001, 002-002
- ring-3: 003-001, 003-002, 003-003
Notes
-----
This function will just define the Zones it sees in the settings, it does
not do any validation against a Core object to ensure those manual zones
make sense.
Returns
-------
Zones
One or more zones, as defined in the `zoneDefinitions` setting.
"""
runLog.debug("Building Zones by manual definitions in `zoneDefinitions` setting")
stripper = lambda s: s.strip()
zones = Zones()

# parse the special input string for zone definitions
for zoneString in cs["zoneDefinitions"]:
zoneName, zoneLocs = zoneString.split(":")
zoneLocs = zoneLocs.split(",")
zone = Zone(zoneName.strip())
zone.addLocs(map(stripper, zoneLocs))
zones.addZone(zone)

if not len(zones):
runLog.debug("No manual zones defined in `zoneDefinitions` setting")

return zones

0 comments on commit 6b50c33

Please sign in to comment.