Skip to content

Commit

Permalink
Clean up report code (#416)
Browse files Browse the repository at this point in the history
This includes a handful of cleanups of the newReports code, mostly
related to things like:
 - general code organization
 - non-standard import locations
 - documentation

In addition, the Case.summarizeDesign() method is changed to use new
reports functionality, rather than needing to create an operator and
interact with the report interface.
  • Loading branch information
youngmit authored Sep 24, 2021
1 parent fcb7843 commit eeb28b4
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 127 deletions.
5 changes: 3 additions & 2 deletions armi/bookkeeping/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,13 @@ def getReportContents(r, cs, report, stage, blueprint):
"""
from armi.cli import reportsEntryPoint
from armi.bookkeeping import newReports as reports
from armi.bookkeeping import newReportUtils

if stage == reportsEntryPoint.ReportStage.Begin:
if stage == reports.ReportStage.Begin:
newReportUtils.insertGeneralReportContent(cs, r, report, stage)
if blueprint is not None:
newReportUtils.insertBlueprintContent(r, cs, report, blueprint)
elif stage == reportsEntryPoint.ReportStage.End:
elif stage == reports.ReportStage.End:
newReportUtils.insertEndOfLifeContent(r, report)
return
3 changes: 1 addition & 2 deletions armi/bookkeeping/newReportUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from armi.utils import plotting
from armi.utils import units
from armi.utils import iterables
from armi.cli.reportsEntryPoint import ReportStage
from armi.materials import custom


Expand All @@ -37,7 +36,7 @@ def insertGeneralReportContent(cs, r, report, stage):
"""
# These items only happen once at BOL

if stage == ReportStage.Begin:
if stage == newReports.ReportStage.Begin:
comprehensiveBOLContent(cs, r, report)
insertDesignContent(r, report)

Expand Down
10 changes: 9 additions & 1 deletion armi/bookkeeping/newReports.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import Enum
from enum import auto
import collections
import shutil
import os
Expand Down Expand Up @@ -86,7 +88,7 @@ def writeReports(self):
),
"report.js",
)
fileurl = doc.renderToFile("ReportContent.html", 0)
fileurl = doc.renderToFile("index.html", 0)
return fileurl

def get(self, section, default=None):
Expand Down Expand Up @@ -566,6 +568,12 @@ def render(self, level, idPrefix="") -> htmltree.HtmlElement:
)


class ReportStage(Enum):
Begin = auto()
Standard = auto()
End = auto()


def encode64(file_path):
"""Encodes the contents of the file indicated by the path
Expand Down
1 change: 0 additions & 1 deletion armi/bookkeeping/report/reportInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def generateDesignReport(self, generateFullCoreMap, showBlockAxMesh):

def interactEOL(self):
"""Adds the data to the report, and generates it"""
self.cs.setSettingsReport()
b = self.o.r.core.getFirstBlock(Flags.FUEL)
b.setAreaFractionsReport()

Expand Down
5 changes: 2 additions & 3 deletions armi/bookkeeping/tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from armi.utils import directoryChangers
from armi.physics.neutronics.reports import neutronicsPlotting
import armi.bookkeeping.newReports
from armi.cli.reportsEntryPoint import ReportStage


class TestReportContentCreation(unittest.TestCase):
Expand Down Expand Up @@ -75,7 +74,7 @@ def testReportContents(self):
r=self.r,
cs=self.o.cs,
report=reportTest,
stage=ReportStage.Begin,
stage=newReports.ReportStage.Begin,
blueprint=self.r.blueprints,
)

Expand Down Expand Up @@ -108,7 +107,7 @@ def testWriteReports(self):
reportTest.writeReports()
# Want to check that two <tr> exists...
times = 0
with open("ReportContent.html") as f:
with open("index.html") as f:
for line in f:
if "<tr>" in line:
times = times + 1
Expand Down
20 changes: 3 additions & 17 deletions armi/cases/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@
from armi import operators
from armi import runLog
from armi import interfaces
from armi.cli import reportsEntryPoint
from armi.reactor import blueprints
from armi.reactor import systemLayoutInput
from armi.reactor import reactors
from armi.bookkeeping import report
from armi.bookkeeping.report import reportInterface
from armi.bookkeeping.db import compareDatabases
from armi.utils import pathTools
from armi.utils.directoryChangers import DirectoryChanger
Expand Down Expand Up @@ -491,21 +490,8 @@ def checkInputs(self):

def summarizeDesign(self, generateFullCoreMap=True, showBlockAxialMesh=True):
"""Uses the ReportInterface to create a fancy HTML page describing the design inputs."""
settings.setMasterCs(self.cs)
o = self.initializeOperator()
with DirectoryChanger(self.cs.inputDirectory, dumpOnException=False):
# There are global variables that are modified when a report is
# generated, so reset it all
six.moves.reload_module(report) # pylint: disable=too-many-function-args
self.cs.setSettingsReport()
rpi = o.getInterface("report")

if rpi is None:
rpi = reportInterface.ReportInterface(o.r, o.cs)

rpi.generateDesignReport(generateFullCoreMap, showBlockAxialMesh)
report.DESIGN.writeHTML()
runLog.important("Design report summary was successfully generated")

_ = reportsEntryPoint.createReportFromSettings(self.cs)

def buildCommand(self, python="python"):
"""
Expand Down
6 changes: 5 additions & 1 deletion armi/cases/tests/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ def test_summarizeDesign(self):
case = cases.Case(cs)
c2 = case.clone()
c2.summarizeDesign(True, True)
self.assertTrue(os.path.exists("Core Design Report.html"))
self.assertTrue(
os.path.exists(
os.path.join("{}-reports".format(c2.cs.caseTitle), "index.html")
)
)

def test_independentVariables(self):
"""Ensure that independentVariables added to a case move with it."""
Expand Down
90 changes: 50 additions & 40 deletions armi/cli/reportsEntryPoint.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from enum import Enum
from enum import auto
import pathlib
import webbrowser

import armi
from armi.cli import entryPoint
from armi.reactor.reactors import factory
from armi.reactor import reactors
from armi.utils import runLog
from armi import settings
from armi.utils import directoryChangers
from armi.reactor import blueprints
from armi.bookkeeping import newReports as reports
from armi.bookkeeping.db import databaseFactory


class ReportsEntryPoint(entryPoint.EntryPoint):
Expand All @@ -23,10 +27,10 @@ def __init__(self):
def addOptions(self):
self.parser.add_argument("-h5db", help="Input database path", type=str)
self.parser.add_argument(
"-bp", help="Input blueprint (optional)", type=str, default=None
"--bp", help="Input blueprint (optional)", type=str, default=None
)
self.parser.add_argument(
"-settings", help="Settings File (optional", type=str, default=None
"--settings", help="Settings File (optional)", type=str, default=None
)
self.parser.add_argument(
"--output-name",
Expand Down Expand Up @@ -67,45 +71,23 @@ def addOptions(self):
# self.createOptionFromSetting("imperialunits", "-i")

def invoke(self):
from armi import settings
from armi.reactor import blueprints
from armi.bookkeeping.newReports import ReportContent
from armi.bookkeeping.db import databaseFactory
from armi.utils import directoryChangers

nodes = self.args.nodes
report = ReportContent("Overview")
app = armi.getApp()
if app is None:
raise RuntimeError("NEED APP!")
pm = app._pm

if self.args.h5db is None:
# Just do begining stuff, no database is given...
if self.cs is not None:
cs = self.cs
settings.setMasterCs(self.cs)
blueprint = blueprints.loadFromCs(cs)
r = factory(cs, blueprint)
report.title = r.name
site = createReportFromSettings(cs)
if self.args.view:
webbrowser.open(site)
else:
raise RuntimeError(
"No Settings with Blueprint or Database, cannot gerenate a report"
)

with directoryChangers.ForcedCreationDirectoryChanger("reportsOutputFiles"):
_ = pm.hook.getReportContents(
r=r,
cs=cs,
report=report,
stage=ReportStage.Begin,
blueprint=blueprint,
)
site = report.writeReports()
if self.args.view:
webbrowser.open(site)

else:
report = reports.ReportContent("Overview")
pm = armi.getPluginManagerOrFail()
db = databaseFactory(self.args.h5db, "r")
if self.args.bp is not None:
blueprint = self.args.bp
Expand All @@ -119,18 +101,18 @@ def invoke(self):
cs = db.loadCS()
if self.args.bp is None:
blueprint = db.loadBlueprints()
r = factory(cs, blueprint)
r = reactors.factory(cs, blueprint)
report.title = r.name
pluginContent = (
armi.getPluginManagerOrFail().hook.getReportContents(
r=r,
cs=cs,
report=report,
stage=ReportStage.Begin,
stage=reports.ReportStage.Begin,
blueprint=blueprint,
)
)
stage = ReportStage.Standard
stage = reports.ReportStage.Standard
for cycle, node in dbNodes:
if nodes is not None and (cycle, node) not in nodes:
continue
Expand All @@ -153,7 +135,7 @@ def invoke(self):
pluginContent = pm.hook.getReportContents(
r=r, cs=cs, report=report, stage=stage, blueprint=blueprint
)
stage = ReportStage.End
stage = reports.ReportStage.End
pluginContent = pm.hook.getReportContents(
r=r, cs=cs, report=report, stage=stage, blueprint=blueprint
)
Expand All @@ -162,7 +144,35 @@ def invoke(self):
webbrowser.open(site)


class ReportStage(Enum):
Begin = auto()
Standard = auto()
End = auto()
def createReportFromSettings(cs):
"""
Create BEGINNING reports, given a settings file.
This will construct a reactor from the given settings and create BOL reports for
that reactor/settings.
"""

# not sure if this is necessary, but need to investigate more to understand possible
# side-effects before removing. Probably better to get rid of all uses of
# getMasterCs(), then we can remove all setMasterCs() calls without worrying.
settings.setMasterCs(cs)

blueprint = blueprints.loadFromCs(cs)
r = reactors.factory(cs, blueprint)
report = reports.ReportContent("Overview")
pm = armi.getPluginManagerOrFail()
report.title = r.name

with directoryChangers.ForcedCreationDirectoryChanger(
"{}-reports".format(cs.caseTitle)
):
_ = pm.hook.getReportContents(
r=r,
cs=cs,
report=report,
stage=reports.ReportStage.Begin,
blueprint=blueprint,
)
site = report.writeReports()

return site
9 changes: 4 additions & 5 deletions armi/physics/neutronics/reports.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from collections import defaultdict

# parts of report for neutronics
from armi.cli.reportsEntryPoint import ReportStage

from armi.bookkeeping import newReportUtils
from armi.bookkeeping import newReports
from armi.reactor.flags import Flags
Expand All @@ -22,10 +19,12 @@ def insertNeutronicsReport(r, cs, report, stage):
collecting contents for.
"""

if stage == ReportStage.Begin:
if stage == newReports.ReportStage.Begin:
insertNeutronicsBOLContent(r, cs, report)

elif stage == ReportStage.Standard or stage == ReportStage.End:
elif (
stage == newReports.ReportStage.Standard or stage == newReports.ReportStage.End
):
neutronicsPlotting(r, report, cs)


Expand Down
22 changes: 14 additions & 8 deletions armi/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,21 +539,27 @@ def mpiActionRequiresReset(cmd) -> bool:

@staticmethod
@HOOKSPEC
def getReportContents(r, cs, report, stage, blueprint): # ReportContent
def getReportContents(r, cs, report, stage, blueprint) -> None:
"""
To generate a report.
For more information, see :doc:`/developer/reports`.
Parameters
----------
r : a reactor
cs : case settings
report : current report object to add to
blueprint : blueprint for a reactor (if None, only partial contents created)
stage : begin/standard/or end (stage of the report for
when inserting BOL vs. EOL content)
r : Reactor
cs : Settings
report : ReportContent
Report object to add contents to
For more information, see the documentation at https://terrapower.github.io/armi/developer/reports.html
stage : ReportStage
begin/standard/or end (stage of the report for when inserting BOL vs. EOL
content)
blueprint : Blueprint, optional
for a reactor (if None, only partial contents created)
"""


Expand Down
Loading

0 comments on commit eeb28b4

Please sign in to comment.