From 51d6442c03eebbfaed12a4c90854021e719abf19 Mon Sep 17 00:00:00 2001 From: jstilley Date: Mon, 15 Apr 2024 14:32:00 -0700 Subject: [PATCH 1/8] Just docstring formatting --- armi/bookkeeping/report/newReportUtils.py | 33 +++++++++++------------ armi/bookkeeping/report/reportingUtils.py | 13 +++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/armi/bookkeeping/report/newReportUtils.py b/armi/bookkeeping/report/newReportUtils.py index 9b8bf0554..ee4da6924 100644 --- a/armi/bookkeeping/report/newReportUtils.py +++ b/armi/bookkeeping/report/newReportUtils.py @@ -45,10 +45,10 @@ def insertGeneralReportContent(cs, r, report, stage): Parameters ---------- - cs : case settings - r : reactor - report : ReportContents object - blueprint : blueprint + cs : armi.settings.caseSettings.Settings + r : Reactor + report : ReportContents + blueprint : Blueprint """ # These items only happen once at BOL if stage == newReports.ReportStage.Begin: @@ -61,7 +61,7 @@ def comprehensiveBOLContent(cs, r, report): Parameters ---------- - cs: Case Settings + cs: armi.settings.caseSettings.Settings r: Reactor report: ReportContent """ @@ -84,7 +84,6 @@ def insertDesignContent(r, report): ---------- r: reactor report: ReportContent - """ report[DESIGN][PIN_ASSEMBLY_DESIGN_SUMMARY] = getPinDesignTable(r.core) @@ -103,14 +102,13 @@ def insertDesignContent(r, report): def insertBlockDesignReport(blueprint, report, cs): - r"""Summarize the block designs from the loading file. + """Summarize the block designs from the loading file. Parameters ---------- blueprint : Blueprint report: ReportContent - cs: Case Settings - + cs: armi.settings.caseSettings.Settings """ report[DESIGN]["Block Summaries"] = newReports.Section("Block Summaries") @@ -170,12 +168,14 @@ def insertBlockDesignReport(blueprint, report, cs): def insertCoreDesignReport(core, cs, report): - r"""Builds report to summarize core design inputs. + """Builds report to summarize core design inputs. Parameters ---------- - core: armi.reactor.reactors.Core + core: armi.reactor.reactors.Core cs: armi.settings.caseSettings.Settings + report : ReportContent + The report to be added to. """ coreDesignTable = newReports.Table("Core Report Table") coreDesignTable.header = ["", "Input Parameter"] @@ -211,7 +211,7 @@ def _setGeneralCoreParametersData(core, cs, coreDesignTable): Parameters ---------- core: Core - cs: Case Settings + cs: armi.settings.caseSettings.Settings coreDesignTable: newReports.Table Current state of table to be added to """ @@ -317,7 +317,6 @@ def insertEndOfLifeContent(r, report): Parameters ---------- r : Reactor - the reactor report : ReportContent The report to be added to. """ @@ -347,7 +346,7 @@ def insertBlockDiagrams(cs, blueprint, report, cold): Parameters ---------- - cs: Case Settings + cs: armi.settings.caseSettings.Settings blueprint: Blueprint report: ReportContent cold: boolean @@ -388,7 +387,7 @@ def insertMetaTable(cs, report): Parameters ---------- - cs: Case Settings + cs: armi.settings.caseSettings.Settings report: ReportContent """ section = report[COMPREHENSIVE_REPORT] @@ -407,7 +406,7 @@ def insertSettingsData(cs, report): Parameters ---------- - cs: Case Settings + cs: armi.settings.caseSettings.Settings report: ReportContent The report to be added to """ @@ -617,7 +616,7 @@ def createDimensionReport(comp): def insertCoreAndAssemblyMaps( r, cs, report, blueprint, generateFullCoreMap=False, showBlockAxMesh=True ): - r"""Create core and assembly design plots. + """Create core and assembly design plots. Parameters ---------- diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index 3a0cfcf3b..f1f9c09ac 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -193,6 +193,13 @@ def _writeMachineInformation(): ) # If this is on Windows: run sys info on each unique node too if "win" in sys.platform: + """Example results: + + OS Name: Microsoft Windows 10 Enterprise + OS Version: 10.0.19041 N/A Build 19041 + Processor(s): 1 Processor(s) Installed. + [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz + """ sysInfoCmd = ( 'systeminfo | findstr /B /C:"OS Name" /B /C:"OS Version" /B ' '/C:"Processor" && systeminfo | findstr /E /C:"Mhz"' @@ -201,6 +208,12 @@ def _writeMachineInformation(): sysInfoCmd, capture_output=True, text=True, shell=True ) sysInfo += out.stdout + elif "linux" in sys.platform: + pass + else: + # TODO: JOHN + pass + runLog.header("=========== Machine Information ===========") runLog.info( tabulate.tabulate( From 9ed81ff8c7142b1751d471a4a3b6d545a6bae645 Mon Sep 17 00:00:00 2001 From: John Stilley Date: Wed, 17 Apr 2024 09:36:33 -0700 Subject: [PATCH 2/8] Placeholder script --- armi/bookkeeping/report/reportingUtils.py | 98 ++++++++++++++++++----- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index f1f9c09ac..720785d5d 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -191,28 +191,17 @@ def _writeMachineInformation(): nodeMappingData.append( (uniqueName, numProcessors, ", ".join(matchingProcs)) ) + # If this is on Windows: run sys info on each unique node too if "win" in sys.platform: - """Example results: - - OS Name: Microsoft Windows 10 Enterprise - OS Version: 10.0.19041 N/A Build 19041 - Processor(s): 1 Processor(s) Installed. - [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz - """ - sysInfoCmd = ( - 'systeminfo | findstr /B /C:"OS Name" /B /C:"OS Version" /B ' - '/C:"Processor" && systeminfo | findstr /E /C:"Mhz"' - ) - out = subprocess.run( - sysInfoCmd, capture_output=True, text=True, shell=True - ) - sysInfo += out.stdout + sysInfo += getSystemInfoWindows() elif "linux" in sys.platform: - pass + sysInfo += getSystemInfoLinux() else: - # TODO: JOHN - pass + runLog.warning( + f"Cannot get system information for {sys.platform} because " + + "ARMI only supports Linux and Windows." + ) runLog.header("=========== Machine Information ===========") runLog.info( @@ -254,6 +243,79 @@ def _writeReactorCycleInformation(o, cs): _writeReactorCycleInformation(o, cs) +def getSystemInfoWindows(): + """TODO. + + Example results: + + OS Name: Microsoft Windows 10 Enterprise + OS Version: 10.0.19041 N/A Build 19041 + Processor(s): 1 Processor(s) Installed. + [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz + """ + sysInfoCmd = ( + 'systeminfo | findstr /B /C:"OS Name" /B /C:"OS Version" /B ' + '/C:"Processor" && systeminfo | findstr /E /C:"Mhz"' + ) + return subprocess.run(sysInfoCmd, capture_output=True, text=True, shell=True) + + +def getSystemInfoLinux(): + """TODO. + + Example results: + + OS Name: Ubuntu + OS Version: Ubuntu 22.04.3 LTS + Processor(s): 2 Processor(s) Installed. + [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + [2]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + """ + # get OS name / version + + # the os-relese file should contain something like: + # PRETTY_NAME="Ubuntu 22.04.3 LTS" + # NAME="Ubuntu" + # VERSION_ID="22.04" + # VERSION="22.04.3 LTS (Jammy Jellyfish)" + # ... + cmd = 'cat /etc/os-release | grep "^NAME="' + os_name = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout + if os_name: + os_name = os_name.split("=")[1].replace('"', "").strip() + + cmd = 'cat /etc/os-release | grep "^PRETTY_NAME="' + os_ver = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout + os_ver = os_ver.split("=")[1].replace('"', "").strip() + else: + os_name = subprocess.run( + "uname -a", capture_output=True, text=True, shell=True + ).stdout.strip() + os_ver = os_name + if not os_name: + raise RuntimeError("What OS is this? TODO JOHN") + + out = "OS Name: " + out += os_name + out += "\nOS Version: " + out += os_ver + out += "\n" + + # get processor information + cmd = "cat /proc/cpuinfo" + proc = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout + proc_num = proc.split("\nprocessor")[-1] + proc_num = int(proc_num.split("\n")[0].split(":")[-1].strip()) + 1 + out += f"Processor(s): {proc_num} Processor(s) Installed.\n" + proc_names = [ + ln.split(":")[-1].strip() for ln in proc.split("\n") if "model name" in ln + ] + for i, pn in enumerate(proc_names): + out += f"{' '*15}[{i+1}]: {pn}\n" + + return out + + def getInterfaceStackSummary(o): data = [] for ii, i in enumerate(o.interfaces, start=1): From 1692d9a74911c14217d24f7867a30cc37a448ff2 Mon Sep 17 00:00:00 2001 From: John Stilley Date: Thu, 18 Apr 2024 09:07:24 -0700 Subject: [PATCH 3/8] Unit tests working --- armi/bookkeeping/report/reportingUtils.py | 132 +++++++++++-------- armi/bookkeeping/report/tests/test_report.py | 66 ++++++++++ 2 files changed, 146 insertions(+), 52 deletions(-) diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index 720785d5d..3000eb5e2 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -192,16 +192,7 @@ def _writeMachineInformation(): (uniqueName, numProcessors, ", ".join(matchingProcs)) ) - # If this is on Windows: run sys info on each unique node too - if "win" in sys.platform: - sysInfo += getSystemInfoWindows() - elif "linux" in sys.platform: - sysInfo += getSystemInfoLinux() - else: - runLog.warning( - f"Cannot get system information for {sys.platform} because " - + "ARMI only supports Linux and Windows." - ) + sysInfo += getSystemInfo runLog.header("=========== Machine Information ===========") runLog.info( @@ -211,6 +202,7 @@ def _writeMachineInformation(): tablefmt="armi", ) ) + if sysInfo: runLog.header("=========== System Information ===========") runLog.info(sysInfo) @@ -243,42 +235,49 @@ def _writeReactorCycleInformation(o, cs): _writeReactorCycleInformation(o, cs) -def getSystemInfoWindows(): - """TODO. +def _getSystemInfoWindows(): + """Get system information, assuming the system is Windows. + + Returns + ------- + str + Basic system information: OS name, OS version, basic processor information + Examples + -------- Example results: - OS Name: Microsoft Windows 10 Enterprise - OS Version: 10.0.19041 N/A Build 19041 - Processor(s): 1 Processor(s) Installed. - [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz + OS Name: Microsoft Windows 10 Enterprise + OS Version: 10.0.19041 N/A Build 19041 + Processor(s): 1 Processor(s) Installed. + [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz """ - sysInfoCmd = ( + cmd = ( 'systeminfo | findstr /B /C:"OS Name" /B /C:"OS Version" /B ' '/C:"Processor" && systeminfo | findstr /E /C:"Mhz"' ) - return subprocess.run(sysInfoCmd, capture_output=True, text=True, shell=True) + return subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout -def getSystemInfoLinux(): - """TODO. +def _getSystemInfoLinux(): + """Get system information, assuming the system is Linux. + Returns + ------- + str + Basic system information: OS name, OS version, basic processor information + + Examples + -------- Example results: - OS Name: Ubuntu - OS Version: Ubuntu 22.04.3 LTS - Processor(s): 2 Processor(s) Installed. - [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz - [2]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + OS Name: Ubuntu + OS Version: Ubuntu 22.04.3 LTS + Processor(s): 2 Processor(s) Installed. + [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + [2]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz """ # get OS name / version - - # the os-relese file should contain something like: - # PRETTY_NAME="Ubuntu 22.04.3 LTS" - # NAME="Ubuntu" - # VERSION_ID="22.04" - # VERSION="22.04.3 LTS (Jammy Jellyfish)" - # ... cmd = 'cat /etc/os-release | grep "^NAME="' os_name = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout if os_name: @@ -288,12 +287,8 @@ def getSystemInfoLinux(): os_ver = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout os_ver = os_ver.split("=")[1].replace('"', "").strip() else: - os_name = subprocess.run( - "uname -a", capture_output=True, text=True, shell=True - ).stdout.strip() - os_ver = os_name - if not os_name: - raise RuntimeError("What OS is this? TODO JOHN") + runLog.warning("OS system info not found.") + return "" out = "OS Name: " out += os_name @@ -304,18 +299,52 @@ def getSystemInfoLinux(): # get processor information cmd = "cat /proc/cpuinfo" proc = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout - proc_num = proc.split("\nprocessor")[-1] - proc_num = int(proc_num.split("\n")[0].split(":")[-1].strip()) + 1 - out += f"Processor(s): {proc_num} Processor(s) Installed.\n" - proc_names = [ - ln.split(":")[-1].strip() for ln in proc.split("\n") if "model name" in ln - ] - for i, pn in enumerate(proc_names): - out += f"{' '*15}[{i+1}]: {pn}\n" + if not proc: + runLog.warning("Processor info not found.") + else: + proc_num = proc.split("\nprocessor")[-1] + proc_num = int(proc_num.split("\n")[0].split(":")[-1].strip()) + 1 + out += f"Processor(s): {proc_num} Processor(s) Installed.\n" + + proc_names = [ + ln.split(":")[-1].strip() for ln in proc.split("\n") if "model name" in ln + ] + for i, pn in enumerate(proc_names): + out += f"{' '*15}[{i+1}]: {pn}\n" return out +def getSystemInfo(): + """Get system information, assuming the system is Windows or Linux. + + Returns + ------- + str + Basic system information: OS name, OS version, basic processor information + + Examples + -------- + Example results: + + OS Name: Ubuntu + OS Version: Ubuntu 22.04.3 LTS + Processor(s): 1 Processor(s) Installed. + [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + """ + # Get basic system information (on Windows and Linux) + if "win" in sys.platform: + return _getSystemInfoWindows() + elif "linux" in sys.platform: + return _getSystemInfoLinux() + else: + runLog.warning( + f"Cannot get system information for {sys.platform} because ARMI only " + + "supports Linux and Windows." + ) + return "" + + def getInterfaceStackSummary(o): data = [] for ii, i in enumerate(o.interfaces, start=1): @@ -465,8 +494,7 @@ def _makeBOLAssemblyMassSummary(massSum): line += "{0:<25.3f}".format(s[val]) str_.append("{0:12s}{1}".format(val, line)) - # print blocks in this assembly - # up to 10 + # print blocks in this assembly up to 10 for i in range(10): line = " " * 12 for s in massSum: @@ -476,6 +504,7 @@ def _makeBOLAssemblyMassSummary(massSum): line += " " * 25 if re.search(r"\S", line): # \S matches any non-whitespace character. str_.append(line) + return "\n".join(str_) @@ -500,10 +529,10 @@ def writeCycleSummary(core): Parameters ---------- - core: armi.reactor.reactors.Core + core: armi.reactor.reactors.Core cs: armi.settings.caseSettings.Settings """ - # would io be worth considering for this? + # Would io be worth considering for this? cycle = core.r.p.cycle str_ = [] runLog.important("Cycle {0} Summary:".format(cycle)) @@ -521,7 +550,6 @@ def setNeutronBalancesReport(core): Parameters ---------- core : armi.reactor.reactors.Core - """ if not core.getFirstBlock().p.rateCap: runLog.warning( @@ -717,7 +745,7 @@ def makeCoreDesignReport(core, cs): Parameters ---------- - core: armi.reactor.reactors.Core + core: armi.reactor.reactors.Core cs: armi.settings.caseSettings.Settings """ coreDesignTable = report.data.Table( diff --git a/armi/bookkeeping/report/tests/test_report.py b/armi/bookkeeping/report/tests/test_report.py index 648d1bde3..0145d8112 100644 --- a/armi/bookkeeping/report/tests/test_report.py +++ b/armi/bookkeeping/report/tests/test_report.py @@ -15,12 +15,17 @@ """Really basic tests of the report Utils.""" import logging import os +import subprocess import unittest +from unittest.mock import patch from armi import runLog, settings from armi.bookkeeping import report from armi.bookkeeping.report import data, reportInterface from armi.bookkeeping.report.reportingUtils import ( + _getSystemInfoLinux, + _getSystemInfoWindows, + getSystemInfo, makeBlockDesignReport, setNeutronBalancesReport, summarizePinDesign, @@ -35,6 +40,67 @@ from armi.utils.directoryChangers import TemporaryDirectoryChanger +class _MockReturnResult: + """Mocking the subproces.run() return object.""" + + def __init__(self, stdout): + self.stdout = stdout + + +class TestReportingUtils(unittest.TestCase): + def test_getSystemInfoLinux(self): + catOsName = 'NAME="Ubuntu"\n' + catOsPrettyName = 'PRETTY_NAME="Ubuntu 22.04.3 LTS"\n' + catProcInfo = """processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 126 +model name : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz +""" + correctResult = """OS Name: Ubuntu +OS Version: Ubuntu 22.04.3 LTS +Processor(s): 1 Processor(s) Installed. + [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz""" + + def __mockSubprocessRun(*args, **kwargs): + if "os-release" in args[0]: + if "PRETTY" in args[0]: + return _MockReturnResult(catOsPrettyName) + else: + return _MockReturnResult(catOsName) + else: + return _MockReturnResult(catProcInfo) + + with patch.object(subprocess, "run", side_effect=__mockSubprocessRun): + out = _getSystemInfoLinux() + self.assertEqual(out.strip(), correctResult) + + @patch("subprocess.run") + def test_getSystemInfoWindows(self, mockSubprocess): + windowsResult = """OS Name: Microsoft Windows 10 Enterprise +OS Version: 10.0.19041 N/A Build 19041 +Processor(s): 1 Processor(s) Installed. + [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz""" + + mockSubprocess.return_value = _MockReturnResult(windowsResult) + + out = _getSystemInfoWindows() + self.assertEqual(out, windowsResult) + + def test_getSystemInfo(self): + """Basic sanity check of getSystemInfo() running in the wild. + + This test should pass if it is run on Window or mainstream Linux distros. But we expect this to fail + if the test is run on some other OS. + """ + out = getSystemInfo() + substrings = ["OS Name:", "OS Version", "Processor(s):"] + for sstr in substrings: + self.assertIn(sstr, out) + + self.assertGreater(len(out), sum(len(sstr) + 3 for sstr in substrings)) + + class TestReport(unittest.TestCase): def setUp(self): self.test_group = data.Table(settings.Settings(), "banana") From aab5f05e1a2595815406ecf334a958dbba3d738f Mon Sep 17 00:00:00 2001 From: John Stilley Date: Thu, 18 Apr 2024 11:41:05 -0700 Subject: [PATCH 4/8] Covering more Linux OSs --- armi/bookkeeping/report/reportingUtils.py | 97 ++++++++++++-------- armi/bookkeeping/report/tests/test_report.py | 35 +++---- 2 files changed, 77 insertions(+), 55 deletions(-) diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index 3000eb5e2..8b0a92c1a 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -271,46 +271,54 @@ def _getSystemInfoLinux(): -------- Example results: - OS Name: Ubuntu - OS Version: Ubuntu 22.04.3 LTS - Processor(s): 2 Processor(s) Installed. - [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz - [2]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + OS Info: Ubuntu 22.04.3 LTS + Processor(s): + processor : 0 + vendor_id : GenuineIntel + cpu family : 6 + model : 126 + model name : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + ... """ # get OS name / version - cmd = 'cat /etc/os-release | grep "^NAME="' - os_name = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout - if os_name: - os_name = os_name.split("=")[1].replace('"', "").strip() - - cmd = 'cat /etc/os-release | grep "^PRETTY_NAME="' - os_ver = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout - os_ver = os_ver.split("=")[1].replace('"', "").strip() - else: - runLog.warning("OS system info not found.") + linuxOsCommands = [ + 'cat /etc/os-release | grep "^PRETTY_NAME=" | cut -d = -f 2', + "uname -a", + "lsb_release -d | cut -d : -f 2", + 'hostnamectl | grep "Operating System" | cut -d : -f 2', + ] + osInfo = "" + for cmd in linuxOsCommands: + osInfo = subprocess.run( + cmd, capture_output=True, text=True, shell=True + ).stdout.strip() + if osInfo: + break + + if not osInfo: + runLog.warning("Linux OS information not found.") return "" - out = "OS Name: " - out += os_name - out += "\nOS Version: " - out += os_ver - out += "\n" - # get processor information - cmd = "cat /proc/cpuinfo" - proc = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout - if not proc: - runLog.warning("Processor info not found.") - else: - proc_num = proc.split("\nprocessor")[-1] - proc_num = int(proc_num.split("\n")[0].split(":")[-1].strip()) + 1 - out += f"Processor(s): {proc_num} Processor(s) Installed.\n" + linuxProcCommands = ["cat /proc/cpuinfo", "lscpu", "lshw -class CPU"] + procInfo = "" + for cmd in linuxProcCommands: + procInfo = subprocess.run( + cmd, capture_output=True, text=True, shell=True + ).stdout + if procInfo: + break + + if not procInfo: + runLog.warning("Linux processor information not found.") + return "" - proc_names = [ - ln.split(":")[-1].strip() for ln in proc.split("\n") if "model name" in ln - ] - for i, pn in enumerate(proc_names): - out += f"{' '*15}[{i+1}]: {pn}\n" + # build output string + out = "OS Info: " + out += osInfo.strip() + out += "\nProcessor(s):\n " + out += procInfo.strip().replace("\n", "\n ") + out += "\n" return out @@ -325,12 +333,23 @@ def getSystemInfo(): Examples -------- - Example results: + Example Windows: + + OS Name: Microsoft Windows 10 Enterprise + OS Version: 10.0.19041 N/A Build 19041 + Processor(s): 1 Processor(s) Installed. + [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz + + Example Linux results: - OS Name: Ubuntu - OS Version: Ubuntu 22.04.3 LTS - Processor(s): 1 Processor(s) Installed. - [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + OS Info: Ubuntu 22.04.3 LTS + Processor(s): + processor : 0 + vendor_id : GenuineIntel + cpu family : 6 + model : 126 + model name : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + ... """ # Get basic system information (on Windows and Linux) if "win" in sys.platform: diff --git a/armi/bookkeeping/report/tests/test_report.py b/armi/bookkeeping/report/tests/test_report.py index 0145d8112..db0c7e374 100644 --- a/armi/bookkeeping/report/tests/test_report.py +++ b/armi/bookkeeping/report/tests/test_report.py @@ -49,27 +49,29 @@ def __init__(self, stdout): class TestReportingUtils(unittest.TestCase): def test_getSystemInfoLinux(self): - catOsName = 'NAME="Ubuntu"\n' - catOsPrettyName = 'PRETTY_NAME="Ubuntu 22.04.3 LTS"\n' - catProcInfo = """processor : 0 + """Test _getSystemInfoLinux() on any operating system, by mocking the system calls.""" + osInfo = '"Ubuntu 22.04.3 LTS"' + procInfo = """processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 126 model name : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz +... """ - correctResult = """OS Name: Ubuntu -OS Version: Ubuntu 22.04.3 LTS -Processor(s): 1 Processor(s) Installed. - [1]: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz""" + correctResult = """OS Info: "Ubuntu 22.04.3 LTS" +Processor(s): + processor : 0 + vendor_id : GenuineIntel + cpu family : 6 + model : 126 + model name : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz + ...""" def __mockSubprocessRun(*args, **kwargs): if "os-release" in args[0]: - if "PRETTY" in args[0]: - return _MockReturnResult(catOsPrettyName) - else: - return _MockReturnResult(catOsName) + return _MockReturnResult(osInfo) else: - return _MockReturnResult(catProcInfo) + return _MockReturnResult(procInfo) with patch.object(subprocess, "run", side_effect=__mockSubprocessRun): out = _getSystemInfoLinux() @@ -77,6 +79,7 @@ def __mockSubprocessRun(*args, **kwargs): @patch("subprocess.run") def test_getSystemInfoWindows(self, mockSubprocess): + """Test _getSystemInfoWindows() on any operating system, by mocking the system call.""" windowsResult = """OS Name: Microsoft Windows 10 Enterprise OS Version: 10.0.19041 N/A Build 19041 Processor(s): 1 Processor(s) Installed. @@ -90,15 +93,15 @@ def test_getSystemInfoWindows(self, mockSubprocess): def test_getSystemInfo(self): """Basic sanity check of getSystemInfo() running in the wild. - This test should pass if it is run on Window or mainstream Linux distros. But we expect this to fail - if the test is run on some other OS. + This test should pass if it is run on Window or mainstream Linux distros. But we expect this + to fail if the test is run on some other OS. """ out = getSystemInfo() - substrings = ["OS Name:", "OS Version", "Processor(s):"] + substrings = ["OS ", "Processor(s):"] for sstr in substrings: self.assertIn(sstr, out) - self.assertGreater(len(out), sum(len(sstr) + 3 for sstr in substrings)) + self.assertGreater(len(out), sum(len(sstr) + 5 for sstr in substrings)) class TestReport(unittest.TestCase): From b7860e9bcf187a4fac3533db2055b7d1df1ff14e Mon Sep 17 00:00:00 2001 From: John Stilley Date: Thu, 18 Apr 2024 11:48:57 -0700 Subject: [PATCH 5/8] Putting this file back to main --- armi/bookkeeping/report/newReportUtils.py | 33 ++++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/armi/bookkeeping/report/newReportUtils.py b/armi/bookkeeping/report/newReportUtils.py index ee4da6924..9b8bf0554 100644 --- a/armi/bookkeeping/report/newReportUtils.py +++ b/armi/bookkeeping/report/newReportUtils.py @@ -45,10 +45,10 @@ def insertGeneralReportContent(cs, r, report, stage): Parameters ---------- - cs : armi.settings.caseSettings.Settings - r : Reactor - report : ReportContents - blueprint : Blueprint + cs : case settings + r : reactor + report : ReportContents object + blueprint : blueprint """ # These items only happen once at BOL if stage == newReports.ReportStage.Begin: @@ -61,7 +61,7 @@ def comprehensiveBOLContent(cs, r, report): Parameters ---------- - cs: armi.settings.caseSettings.Settings + cs: Case Settings r: Reactor report: ReportContent """ @@ -84,6 +84,7 @@ def insertDesignContent(r, report): ---------- r: reactor report: ReportContent + """ report[DESIGN][PIN_ASSEMBLY_DESIGN_SUMMARY] = getPinDesignTable(r.core) @@ -102,13 +103,14 @@ def insertDesignContent(r, report): def insertBlockDesignReport(blueprint, report, cs): - """Summarize the block designs from the loading file. + r"""Summarize the block designs from the loading file. Parameters ---------- blueprint : Blueprint report: ReportContent - cs: armi.settings.caseSettings.Settings + cs: Case Settings + """ report[DESIGN]["Block Summaries"] = newReports.Section("Block Summaries") @@ -168,14 +170,12 @@ def insertBlockDesignReport(blueprint, report, cs): def insertCoreDesignReport(core, cs, report): - """Builds report to summarize core design inputs. + r"""Builds report to summarize core design inputs. Parameters ---------- - core: armi.reactor.reactors.Core + core: armi.reactor.reactors.Core cs: armi.settings.caseSettings.Settings - report : ReportContent - The report to be added to. """ coreDesignTable = newReports.Table("Core Report Table") coreDesignTable.header = ["", "Input Parameter"] @@ -211,7 +211,7 @@ def _setGeneralCoreParametersData(core, cs, coreDesignTable): Parameters ---------- core: Core - cs: armi.settings.caseSettings.Settings + cs: Case Settings coreDesignTable: newReports.Table Current state of table to be added to """ @@ -317,6 +317,7 @@ def insertEndOfLifeContent(r, report): Parameters ---------- r : Reactor + the reactor report : ReportContent The report to be added to. """ @@ -346,7 +347,7 @@ def insertBlockDiagrams(cs, blueprint, report, cold): Parameters ---------- - cs: armi.settings.caseSettings.Settings + cs: Case Settings blueprint: Blueprint report: ReportContent cold: boolean @@ -387,7 +388,7 @@ def insertMetaTable(cs, report): Parameters ---------- - cs: armi.settings.caseSettings.Settings + cs: Case Settings report: ReportContent """ section = report[COMPREHENSIVE_REPORT] @@ -406,7 +407,7 @@ def insertSettingsData(cs, report): Parameters ---------- - cs: armi.settings.caseSettings.Settings + cs: Case Settings report: ReportContent The report to be added to """ @@ -616,7 +617,7 @@ def createDimensionReport(comp): def insertCoreAndAssemblyMaps( r, cs, report, blueprint, generateFullCoreMap=False, showBlockAxMesh=True ): - """Create core and assembly design plots. + r"""Create core and assembly design plots. Parameters ---------- From ff10e6aea1ae68a11756e101975ffb37fe816db4 Mon Sep 17 00:00:00 2001 From: John Stilley Date: Thu, 18 Apr 2024 11:53:42 -0700 Subject: [PATCH 6/8] Improving documentation --- armi/bookkeeping/report/reportingUtils.py | 29 +++++++---------------- doc/release/0.3.rst | 1 + 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index 8b0a92c1a..6f7fabf67 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -262,6 +262,11 @@ def _getSystemInfoWindows(): def _getSystemInfoLinux(): """Get system information, assuming the system is Linux. + This method uses multiple, redundant variations on common Linux command utilities to get the + information necessary. While it is not possible to guarantee what programs or files will be + available on "all Linux operating system", this collection of tools is widely supported and + should provide a reasonably broad-distribution coverage. + Returns ------- str @@ -326,30 +331,14 @@ def _getSystemInfoLinux(): def getSystemInfo(): """Get system information, assuming the system is Windows or Linux. + Notes + ----- + The format of the system information will be different on Windows vs Linux. + Returns ------- str Basic system information: OS name, OS version, basic processor information - - Examples - -------- - Example Windows: - - OS Name: Microsoft Windows 10 Enterprise - OS Version: 10.0.19041 N/A Build 19041 - Processor(s): 1 Processor(s) Installed. - [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~801 Mhz - - Example Linux results: - - OS Info: Ubuntu 22.04.3 LTS - Processor(s): - processor : 0 - vendor_id : GenuineIntel - cpu family : 6 - model : 126 - model name : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz - ... """ # Get basic system information (on Windows and Linux) if "win" in sys.platform: diff --git a/doc/release/0.3.rst b/doc/release/0.3.rst index bb58d291d..38fb9ef63 100644 --- a/doc/release/0.3.rst +++ b/doc/release/0.3.rst @@ -9,6 +9,7 @@ Release Date: TBD New Features ------------ #. Conserve mass by component in assembly.setBlockMesh(). (`PR#1665 `_) +#. System information is now also logged on Linux. (`PR#1689 `_) #. TBD API Changes From b38818d35c4bf59b5113287864ed49b869ecb2ca Mon Sep 17 00:00:00 2001 From: John Stilley <1831479+john-science@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:40:47 -0700 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Arrielle Opotowsky --- armi/bookkeeping/report/reportingUtils.py | 2 +- armi/bookkeeping/report/tests/test_report.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index 6f7fabf67..4ad1dd23d 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -192,7 +192,7 @@ def _writeMachineInformation(): (uniqueName, numProcessors, ", ".join(matchingProcs)) ) - sysInfo += getSystemInfo + sysInfo += getSystemInfo() runLog.header("=========== Machine Information ===========") runLog.info( diff --git a/armi/bookkeeping/report/tests/test_report.py b/armi/bookkeeping/report/tests/test_report.py index db0c7e374..f96eecaf1 100644 --- a/armi/bookkeeping/report/tests/test_report.py +++ b/armi/bookkeeping/report/tests/test_report.py @@ -41,7 +41,7 @@ class _MockReturnResult: - """Mocking the subproces.run() return object.""" + """Mocking the subprocess.run() return object.""" def __init__(self, stdout): self.stdout = stdout From 2396eccd289445cd5a3b8a3d2adb4291c23ed81d Mon Sep 17 00:00:00 2001 From: John Stilley Date: Mon, 22 Apr 2024 11:16:24 -0700 Subject: [PATCH 8/8] Fixing issue with machine name --- armi/bookkeeping/report/reportingUtils.py | 26 +++++++++++++++++++- armi/bookkeeping/report/tests/test_report.py | 10 ++++++++ armi/context.py | 5 ++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/armi/bookkeeping/report/reportingUtils.py b/armi/bookkeeping/report/reportingUtils.py index 4ad1dd23d..2ce2c6b24 100644 --- a/armi/bookkeeping/report/reportingUtils.py +++ b/armi/bookkeeping/report/reportingUtils.py @@ -78,7 +78,7 @@ def _writeCaseInformation(o, cs): (Operator_ArmiCodebase, context.ROOT), (Operator_WorkingDirectory, os.getcwd()), (Operator_PythonInterperter, sys.version), - (Operator_MasterMachine, os.environ.get("COMPUTERNAME", "?")), + (Operator_MasterMachine, getNodeName()), (Operator_NumProcessors, context.MPI_SIZE), (Operator_Date, context.START_TIME), ] @@ -235,6 +235,30 @@ def _writeReactorCycleInformation(o, cs): _writeReactorCycleInformation(o, cs) +def getNodeName(): + """Get the name of this comput node. + + First, look in context.py. Then try various Linux tools. Then try Windows commands. + + Returns + ------- + str + Compute node name. + """ + hostNames = [ + context.MPI_NODENAME, + context.MPI_NODENAMES[0], + subprocess.run("hostname", capture_output=True, text=True, shell=True).stdout, + subprocess.run("uname -n", capture_output=True, text=True, shell=True).stdout, + os.environ.get("COMPUTERNAME", context.LOCAL), + ] + for nodeName in hostNames: + if nodeName and nodeName != context.LOCAL: + return nodeName + + return context.LOCAL + + def _getSystemInfoWindows(): """Get system information, assuming the system is Windows. diff --git a/armi/bookkeeping/report/tests/test_report.py b/armi/bookkeeping/report/tests/test_report.py index f96eecaf1..87298060e 100644 --- a/armi/bookkeeping/report/tests/test_report.py +++ b/armi/bookkeeping/report/tests/test_report.py @@ -25,6 +25,7 @@ from armi.bookkeeping.report.reportingUtils import ( _getSystemInfoLinux, _getSystemInfoWindows, + getNodeName, getSystemInfo, makeBlockDesignReport, setNeutronBalancesReport, @@ -103,6 +104,15 @@ def test_getSystemInfo(self): self.assertGreater(len(out), sum(len(sstr) + 5 for sstr in substrings)) + def test_getNodeName(self): + """Test that the getNodeName() method returns a non-empty string. + + It is hard to know what string SHOULD be return here, and it would depend on how the OS is + set up on your machine or cluster. But this simple test needs to pass as-is on Windows + and Linux. + """ + self.assertGreater(len(getNodeName()), 0) + class TestReport(unittest.TestCase): def setUp(self): diff --git a/armi/context.py b/armi/context.py index 49b8c43aa..4927b0254 100644 --- a/armi/context.py +++ b/armi/context.py @@ -104,8 +104,9 @@ def setMode(cls, mode): # MPI_SIZE is the total number of CPUs MPI_RANK = 0 MPI_SIZE = 1 -MPI_NODENAME = "local" -MPI_NODENAMES = ["local"] +LOCAL = "local" +MPI_NODENAME = LOCAL +MPI_NODENAMES = [LOCAL] try: