Skip to content

Commit

Permalink
Fixing DB write out for coupling (#1005)
Browse files Browse the repository at this point in the history
  • Loading branch information
albeanth committed Dec 14, 2022
1 parent 8919afd commit 7a179ad
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 35 deletions.
12 changes: 12 additions & 0 deletions armi/bookkeeping/db/databaseInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,19 @@ def interactEveryNode(self, cycle, node):
Write to database.
DBs should receive the state information of the run at each node.
Notes
-----
- if tight coupling is enabled, the DB will be written in operator.py::Operator::_timeNodeLoop
via writeDBEveryNode
"""
if self.o.cs["numCoupledIterations"]:
# h5 cant handle overwriting so we skip here and write once the tight coupling loop has completed
return
self.writeDBEveryNode(cycle, node)

def writeDBEveryNode(self, cycle, node):
"""write the database at the end of the time node"""
# skip writing for last burn step since it will be written at interact EOC
if node < self.o.burnSteps[cycle]:
self.r.core.p.minutesSinceStart = (
Expand Down
15 changes: 15 additions & 0 deletions armi/bookkeeping/db/tests/test_databaseInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ def tearDown(self):
self.stateRetainer.__exit__()
self.td.__exit__(None, None, None)

def test_interactEveryNodeReturn(self):
"""test that the DB is NOT written to if cs["numCoupledIterations"] != 0"""
self.o.cs["numCoupledIterations"] = 2
self.dbi.interactEveryNode(0, 0)
self.assertFalse(self.dbi.database.hasTimeStep(0, 0))

def test_interactBOL(self):
self.assertIsNotNone(self.dbi._db)
self.dbi.interactBOL()
Expand All @@ -115,6 +121,15 @@ def test_distributable(self):
self.dbi.interactDistributeState()
self.assertEqual(self.dbi.distributable(), 4)

def test_timeNodeLoop_numCoupledIterations(self):
"""test that database is written out after the coupling loop has completed"""
# clear out interfaces (no need to run physics) but leave database
self.o.interfaces = [self.dbi]
self.o.cs["numCoupledIterations"] = 1
self.assertFalse(self.dbi._db.hasTimeStep(0, 0))
self.o._timeNodeLoop(0, 0)
self.assertTrue(self.dbi._db.hasTimeStep(0, 0))


class TestDatabaseWriter(unittest.TestCase):
def setUp(self):
Expand Down
3 changes: 3 additions & 0 deletions armi/operators/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@ def _timeNodeLoop(self, cycle, timeNode):
for coupledIteration in range(self.cs["numCoupledIterations"]):
self.r.core.p.coupledIteration = coupledIteration + 1
self.interactAllCoupled(coupledIteration)
# database has not yet been written, so we need to write it.
dbi = self.getInterface("database")
dbi.writeDBEveryNode(cycle, timeNode)

def _interactAll(self, interactionName, activeInterfaces, *args):
"""
Expand Down
67 changes: 32 additions & 35 deletions armi/operators/tests/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

"""Tests for operators"""

# pylint: disable=abstract-method,no-self-use,unused-argument
# pylint: disable=abstract-method,protected-access,unused-argument
import unittest

from armi import settings
Expand Down Expand Up @@ -44,78 +44,75 @@ class InterfaceC(Interface):

# TODO: Add a test that shows time evolution of Reactor (REQ_EVOLVING_STATE)
class OperatorTests(unittest.TestCase):
def setUp(self):
self.o, self.r = test_reactors.loadTestReactor()

def test_addInterfaceSubclassCollision(self):
self.cs = settings.Settings()
o, r = test_reactors.loadTestReactor()
cs = settings.Settings()

interfaceA = InterfaceA(r, self.cs)
interfaceA = InterfaceA(self.r, cs)

interfaceB = InterfaceB(r, self.cs)
o.addInterface(interfaceA)
interfaceB = InterfaceB(self.r, cs)
self.o.addInterface(interfaceA)

# 1) Adds B and gets rid of A
o.addInterface(interfaceB)
self.assertEqual(o.getInterface("Second"), interfaceB)
self.assertEqual(o.getInterface("First"), None)
self.o.addInterface(interfaceB)
self.assertEqual(self.o.getInterface("Second"), interfaceB)
self.assertEqual(self.o.getInterface("First"), None)

# 2) Now we have B which is a subclass of A,
# we want to not add A (but also not have an error)
o.addInterface(interfaceA)
self.assertEqual(o.getInterface("Second"), interfaceB)
self.assertEqual(o.getInterface("First"), None)
self.o.addInterface(interfaceA)
self.assertEqual(self.o.getInterface("Second"), interfaceB)
self.assertEqual(self.o.getInterface("First"), None)

# 3) Also if another class not a subclass has the same function,
# raise an error
interfaceC = InterfaceC(r, self.cs)
self.assertRaises(RuntimeError, o.addInterface, interfaceC)
interfaceC = InterfaceC(self.r, cs)
self.assertRaises(RuntimeError, self.o.addInterface, interfaceC)

# 4) Check adding a different function Interface
interfaceC.function = "C"
o.addInterface(interfaceC)
self.assertEqual(o.getInterface("Second"), interfaceB)
self.assertEqual(o.getInterface("Third"), interfaceC)
self.o.addInterface(interfaceC)
self.assertEqual(self.o.getInterface("Second"), interfaceB)
self.assertEqual(self.o.getInterface("Third"), interfaceC)

def test_checkCsConsistency(self):
o, _r = test_reactors.loadTestReactor()
o._checkCsConsistency() # passes without error
self.o._checkCsConsistency() # passes without error

o.cs = o.cs.modified(newSettings={"nCycles": 66})
self.o.cs = self.o.cs.modified(newSettings={"nCycles": 66})
with self.assertRaises(RuntimeError):
o._checkCsConsistency()
self.o._checkCsConsistency()

def test_interfaceIsActive(self):
o, _r = test_reactors.loadTestReactor()
self.assertTrue(o.interfaceIsActive("main"))
self.assertFalse(o.interfaceIsActive("Fake-o"))
self.o, _r = test_reactors.loadTestReactor()
self.assertTrue(self.o.interfaceIsActive("main"))
self.assertFalse(self.o.interfaceIsActive("Fake-o"))

def test_loadStateError(self):
"""The loadTestReactor() test tool does not have any history in the DB to load from"""
o, _r = test_reactors.loadTestReactor()

# a first, simple test that this method fails correctly
with self.assertRaises(RuntimeError):
o.loadState(0, 1)
self.o.loadState(0, 1)

def test_couplingIsActive(self):
o, _r = test_reactors.loadTestReactor()
self.assertFalse(o.couplingIsActive())
self.assertFalse(self.o.couplingIsActive())

def test_setStateToDefault(self):
o, _r = test_reactors.loadTestReactor()

# reset the runType for testing
self.assertEqual(o.cs["runType"], "Standard")
o.cs = o.cs.modified(newSettings={"runType": "fake"})
self.assertEqual(o.cs["runType"], "fake")
self.assertEqual(self.o.cs["runType"], "Standard")
self.o.cs = self.o.cs.modified(newSettings={"runType": "fake"})
self.assertEqual(self.o.cs["runType"], "fake")

# validate the method works
cs = o.setStateToDefault(o.cs)
cs = self.o.setStateToDefault(self.o.cs)
self.assertEqual(cs["runType"], "Standard")

def test_snapshotRequest(self):
o, _r = test_reactors.loadTestReactor()
with TemporaryDirectoryChanger():
o.snapshotRequest(0, 1)
self.o.snapshotRequest(0, 1)


class CyclesSettingsTests(unittest.TestCase):
Expand Down
1 change: 1 addition & 0 deletions doc/release/0.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Bug fixes
---------
#. Fixed bug in ``referenceBlockAxialMesh`` and ``axialMesh`` during process loading. (`PR#980 <https://github.com/terrapower/armi/pull/980>`)
#. Removed Barriers in temp directory changers and output cache to avoid deadlocks in MPI cases
#. Fixed bug with database writing and tight coupling. (`PR#1005 https://github.com/terrapower/armi/pull/1005`)


ARMI v0.2.5
Expand Down

0 comments on commit 7a179ad

Please sign in to comment.