Skip to content

Commit

Permalink
remove reactors.buildZones; move zones.buildManualZones to reactors m…
Browse files Browse the repository at this point in the history
…odule
  • Loading branch information
joshuavictorchen committed Oct 12, 2022
1 parent ccc6b7b commit 3405bde
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 181 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
44 changes: 43 additions & 1 deletion 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 @@ -2303,3 +2303,45 @@ def processLoading(self, cs, dbLoad: bool = False):
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")
10 changes: 4 additions & 6 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 Down Expand Up @@ -272,7 +270,7 @@ def test_buildManualZones(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)

zonez = self.r.core.zones
self.assertEqual(len(list(zonez)), 3)
Expand All @@ -286,7 +284,7 @@ def test_buildManualZonesEmpty(self):
cs = self.o.cs.modified(newSettings=newSettings)

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

def test_sortZones(self):
Expand Down
81 changes: 0 additions & 81 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 @@ -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
78 changes: 0 additions & 78 deletions armi/tests/test_user_plugins.py
Expand Up @@ -109,25 +109,6 @@ def onProcessCoreLoading(core, cs):
b.p.height += 1.0


class UserPluginDefineZoningStrategy(plugins.UserPlugin):
"""
This plugin flex-tests the applyZoningStrategy() hook,
and puts every Assembly into its own Zone.
"""

@staticmethod
@plugins.HOOKIMPL
def applyZoningStrategy(core, cs):
core.zones = zones.Zones()
assems = core.getAssemblies()
for a in assems:
loc = a.getLocation()
z = zones.Zone(name=loc, locations=[loc], zoneType=Assembly)
core.zones.addZone(z)

return len(core.zones)


class UpInterface(interfaces.Interface):
"""
A mostly meaningless little test interface, just to prove that we can affect
Expand Down Expand Up @@ -269,65 +250,6 @@ def test_userPluginOnProcessCoreLoading(self):
for i, height in enumerate(heights):
self.assertEqual(fuels[i].p.height, height + 1.0)

def test_userPluginDefineZoningStrategy(self):
"""
Test that a UserPlugin can affect the Reactor state,
by implementing applyZoningStrategy() to arbitrarily put each
Assembly in the test reactor into its own Zone.
"""
# register the plugin
app = getApp()
name = "UserPluginDefineZoningStrategy"

# register the zoning plugin
pluginNames = [p[0] for p in app.pluginManager.list_name_plugin()]
self.assertNotIn(name, pluginNames)
app.pluginManager.register(UserPluginDefineZoningStrategy)

# also register another plugin, to test a more complicated situation
app.pluginManager.register(UserPluginOnProcessCoreLoading)

# validate the plugins was registered
pluginz = app.pluginManager.list_name_plugin()
pluginNames = [p[0] for p in pluginz]
self.assertIn(name, pluginNames)

# load a reactor and grab the fuel assemblies
o, r = test_reactors.loadTestReactor(TEST_ROOT)

# prove that our plugin affects the core in the desired way
self.assertEqual(len(r.core.zones), len(r.core.getAssemblies()))
name0 = r.core.zones.names[0]
self.assertIn(name0, r.core.zones[name0])

def test_userPluginDefineZoningStrategyMultipleFail(self):
"""Ensure that multiple plugins registering Zoning stragies raises an Error"""

class DuplicateZoner(UserPluginDefineZoningStrategy):
pass

# register the plugin
app = getApp()
name0 = "UserPluginDefineZoningStrategy"
name1 = "DuplicateZoner"

# register the zoning plugin
pluginNames = [p[0] for p in app.pluginManager.list_name_plugin()]
self.assertNotIn(name0, pluginNames)
self.assertNotIn(name1, pluginNames)
app.pluginManager.register(UserPluginDefineZoningStrategy)
app.pluginManager.register(DuplicateZoner)

# validate the plugins was registered
pluginz = app.pluginManager.list_name_plugin()
pluginNames = [p[0] for p in pluginz]
self.assertIn(name0, pluginNames)
self.assertIn(name1, pluginNames)

# trying to load a Reactor should raise an error
with self.assertRaises(RuntimeError):
o, r = test_reactors.loadTestReactor(TEST_ROOT)

def test_userPluginWithInterfaces(self):
"""Test that UserPlugins can correctly inject an interface into the stack"""
# register the plugin
Expand Down

0 comments on commit 3405bde

Please sign in to comment.