Skip to content

Commit 7104104

Browse files
authored
Merge pull request #312 from LLNL/bugfix/fix_none_type_options
Parsing and testing improvements
2 parents 50eec5f + 2148816 commit 7104104

File tree

72 files changed

+439
-325
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+439
-325
lines changed

RELEASE_NOTES.md

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Notable changes include:
4848
* Clang C++ warnings have eliminated, so the Clang CI tests have been updated to treat warnings as errors.
4949
* Fix for installing libraries when building individual package WITH ENABLE_DEV_BUILD=On.
5050
* Bugfix for RZ solid CRKSPH with compatible energy.
51+
* Parsing of None string now always becomes None python type. Tests have been updated accordingly.
52+
* IO for checkpoints and visuzalization can now be properly turned off through SpheralController input options.
5153
5254
Version v2024.06.1 -- Release date 2024-07-09
5355
==============================================

src/PYB11/Utilities/Utilities_PYB11.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ def clippedVolume(poly = "const Dim<3>::FacetedVolume&",
819819
("long", "Long"),
820820
("double", "Scalar"),
821821
("std::string", "String")):
822-
exec("""
823-
adiak_value%(label)s = PYB11TemplateFunction(adiak_value, "%(value)s")
824-
adiak_value2%(label)s = PYB11TemplateFunction(adiak_value2, "%(value)s", pyname="adiak_value%(label)s")
825-
""" % {"label" : label, "value" : value})
822+
exec(f"""
823+
adiak_value{label} = PYB11TemplateFunction(adiak_value, "{value}", pyname="adiak_value")
824+
adiak_value2{label} = PYB11TemplateFunction(adiak_value2, "{value}", pyname="adiak_value")
825+
""")

src/SimulationControl/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ spheral_install_python_files(
6060
SpheralPolytopeSiloDump.py
6161
Spheral1dVizDump.py
6262
SpheralMatplotlib.py
63+
SpheralTimingParser.py
6364
findLastRestart.py
6465
Pnorm.py
6566
filearraycmp.py

src/SimulationControl/SpheralController.py

+20-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from SpheralCompiledPackages import *
77
from SpheralTimer import SpheralTimer
8+
from SpheralUtilities import adiak_value
89
from SpheralConservation import SpheralConservation
910
from GzipFileIO import GzipFileIO
1011
from SpheralTestUtilities import globalFrame
@@ -52,6 +53,7 @@ def __init__(self, integrator,
5253
volumeType = RKVolumeType.RKVoronoiVolume,
5354
facetedBoundaries = None,
5455
printAllTimers = False):
56+
self.restartBaseName = restartBaseName
5557
self.restart = RestartableObject(self)
5658
self.integrator = integrator
5759
self.restartObjects = restartObjects
@@ -80,6 +82,7 @@ def __init__(self, integrator,
8082

8183
# Determine the dimensionality of this run, based on the integrator.
8284
self.dim = "%id" % self.integrator.dataBase.nDim
85+
adiak_value("dim", self.dim)
8386

8487
# Determine the visualization method.
8588
if self.dim == "1d":
@@ -100,8 +103,11 @@ def __init__(self, integrator,
100103
self.insertDistributedBoundary(integrator.physicsPackages())
101104

102105
# Should we look for the last restart set?
103-
if restoreCycle == -1:
104-
restoreCycle = findLastRestart(restartBaseName)
106+
if restartBaseName:
107+
if restoreCycle == -1:
108+
restoreCycle = findLastRestart(restartBaseName)
109+
else:
110+
restoreCycle = None
105111

106112
# Generic initialization work.
107113
self.reinitializeProblem(restartBaseName,
@@ -181,7 +187,8 @@ def reinitializeProblem(self, restartBaseName, vizBaseName,
181187
self._periodicTimeWork = []
182188

183189
# Set the restart file base name.
184-
self.setRestartBaseName(restartBaseName)
190+
if restartBaseName:
191+
self.setRestartBaseName(restartBaseName)
185192

186193
# Set the simulation time.
187194
self.integrator.currentTime = initialTime
@@ -393,9 +400,12 @@ def advance(self, goalTime, maxSteps=None):
393400
numActualGhostNodes = 0
394401
for bc in bcs:
395402
numActualGhostNodes += bc.numGhostNodes
396-
print("Total number of (internal, ghost, active ghost) nodes : (%i, %i, %i)" % (mpi.allreduce(db.numInternalNodes, mpi.SUM),
397-
mpi.allreduce(db.numGhostNodes, mpi.SUM),
398-
mpi.allreduce(numActualGhostNodes, mpi.SUM)))
403+
numInternal = db.globalNumInternalNodes
404+
numGhost = db.globalNumGhostNodes
405+
numActGhost = mpi.allreduce(numActualGhostNodes, mpi.SUM)
406+
print(f"Total number of (internal, ghost, active ghost) nodes : ({numInternal}, {numGhost}, {numActGhost})")
407+
adiak_value("total_internal_nodes", numInternal)
408+
adiak_value("total_ghost_nodes", numGhost)
399409

400410
# Print how much time was spent per integration cycle.
401411
self.stepTimer.printStatus()
@@ -566,6 +576,8 @@ def findname(thing):
566576
#--------------------------------------------------------------------------
567577
def dropRestartFile(self):
568578

579+
if not self.restartBaseName:
580+
return
569581
# First find out if the requested directory exists.
570582
import os
571583
dire = os.path.dirname(os.path.abspath(self.restartBaseName))
@@ -594,6 +606,8 @@ def dropRestartFile(self):
594606
def loadRestartFile(self, restoreCycle,
595607
frameDict=None):
596608

609+
if not self.restartBaseName:
610+
return
597611
# Find out if the requested file exists.
598612
import os
599613
fileName = self.restartBaseName + "_cycle%i" % restoreCycle

src/SimulationControl/SpheralOptionParser.py

+23-52
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,41 @@
77
from SpheralCompiledPackages import *
88

99
from SpheralTestUtilities import globalFrame
10-
from SpheralUtilities import TimerMgr
10+
import SpheralTimingParser
11+
12+
def parse_value(value):
13+
gd = globalFrame().f_globals
14+
try:
15+
return eval(value, gd)
16+
except:
17+
return value
1118

1219
def commandLine(**options):
1320

1421
# Build a command line parser with the keyword arguments passed to us.
1522
parser = argparse.ArgumentParser()
16-
for key in options:
17-
parser.add_argument("--" + key,
18-
dest = key,
19-
default = options[key])
23+
for key, default in options.items():
24+
if default == "None":
25+
raise SyntaxError(f"ERROR: {key}, None as a default value cannot be a string")
26+
elif type(default) is str:
27+
parser.add_argument(f"--{key}", type = str, default = default)
28+
else:
29+
parser.add_argument(f"--{key}", type = parse_value, default = default)
2030

2131
# Add the universal options supported by all Spheral++ scripts.
2232
parser.add_argument("-v", "--verbose",
2333
action = "store_true",
2434
dest = "verbose",
2535
default = False,
2636
help = "Verbose output -- print all options that were set.")
27-
parser.add_argument("--caliperConfig", default="", type=str)
28-
parser.add_argument("--caliperFilename", default="", type=str)
29-
parser.add_argument("--caliperConfigJSON", default="", type=str)
37+
38+
# Parse Caliper and Adiak inputs
39+
SpheralTimingParser.add_timing_args(parser)
40+
3041
# Evaluate the command line.
3142
args = parser.parse_args()
3243
arg_dict = vars(args)
3344

34-
if (not TimerMgr.timers_usable()):
35-
if (args.caliperConfig or args.caliperFilename or args.caliperConfigJSON):
36-
print("WARNING: Caliper command line inputs provided for "+\
37-
"non-timer install. Reconfigure the install with "+\
38-
"-DENABLE_TIMER=ON to be able to use Caliper timers.")
39-
4045
# Verbose output?
4146
if args.verbose:
4247
print("All parameters set:")
@@ -46,46 +51,12 @@ def commandLine(**options):
4651
print(" * ", key, " = ", val)
4752
else:
4853
print(" ", key, " = ", val)
49-
if (args.caliperConfig):
50-
print(" * caliperConfig = ", args.caliperConfig)
51-
if (args.caliperFilename):
52-
print(" * caliperFilename = ", args.caliperFilename)
53-
if (args.caliperConfigJSON):
54-
print(" * caliperConfigJSON = ", args.caliperConfigJSON)
5554
# Set all the variables.
5655
gd = globalFrame().f_globals
5756
for key, val in arg_dict.items():
58-
if key in options:
59-
if (type(val) != type(options[key])):
60-
val = eval(val, gd)
57+
if val == "None":
58+
val = None
6159
gd[key] = val
62-
# Initialize timers
63-
InitTimers(args.caliperConfig, args.caliperFilename, args.caliperConfigJSON)
64-
return
65-
66-
def InitTimers(caliper_config, filename, caliper_json):
67-
if(caliper_json):
68-
TimerMgr.load(caliper_json)
69-
if(not caliper_config):
70-
raise RuntimeError("SpheralOptionParser: specifying a configuration file without using one of the configurations means no timers are started")
71-
off_tests = ["none", "off", "disable", "disabled", "0"]
72-
if (caliper_config.lower() in off_tests):
73-
return
74-
elif (caliper_config):
75-
TimerMgr.add(caliper_config)
76-
TimerMgr.start()
77-
else:
78-
import os, sys
79-
if (filename):
80-
testname = filename
81-
else:
82-
from datetime import datetime
83-
# Append the current day and time to the filename
84-
unique_digits = datetime.now().strftime("_%Y_%m_%d_%H%M%S_%f")
85-
# Name file based on name of python file being run
86-
testname = os.path.splitext(os.path.basename(sys.argv[0]))[0]
87-
testname += unique_digits + ".cali"
88-
TimerMgr.default_start(testname)
89-
adiak_valueInt("threads_per_rank", omp_get_num_threads())
90-
adiak_valueInt("num_ranks", mpi.procs)
60+
# Initialize timers and add inputs as Adiak metadata
61+
SpheralTimingParser.init_timer(args)
9162
return
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#-------------------------------------------------------------------------------
2+
# Functions for adding Caliper and Adiak parsing arguments and initializing
3+
# the timer manager
4+
#-------------------------------------------------------------------------------
5+
6+
import argparse, mpi
7+
from SpheralUtilities import TimerMgr
8+
from SpheralUtilities import adiak_value
9+
import SpheralOpenMP
10+
11+
cali_args = ["caliperConfig", "caliperFilename", "caliperConfigJSON"]
12+
13+
def parse_dict(string):
14+
"""
15+
Function to parse a dictionary provided through the command line
16+
"""
17+
try:
18+
inp_dict = dict(item.split(":") for item in string.split(","))
19+
except:
20+
raise SyntaxError("Input to --adiakData must be in key:value format, separated by commas")
21+
new_dict = {}
22+
for ikey, ival in inp_dict.items():
23+
try:
24+
key = eval(ikey)
25+
except:
26+
key = ikey.strip()
27+
try:
28+
val = eval(ival)
29+
except:
30+
val = ival.strip()
31+
new_dict.update({key: val})
32+
return new_dict
33+
34+
def add_timing_args(parser):
35+
"""
36+
Add Caliper and Adiak arguments to the parser
37+
"""
38+
# Allow Adiak values to be set on the command line
39+
# Inputs are a string that can be evaluated into a dictionary
40+
# For example, --adiakData "testname: ShockTube1, testing:3"
41+
parser.add_argument("--adiakData", default=None,
42+
type=parse_dict)
43+
# This logic checks if the user already set a Caliper
44+
# argument and default value and prevents adding the argument
45+
# if it already exists
46+
arg_list = [action.dest for action in parser._actions]
47+
for ca in cali_args:
48+
if (ca not in arg_list):
49+
parser.add_argument(f"--{ca}", default="", type=str)
50+
51+
def init_timer(args):
52+
"""
53+
Initializes the timing manager and adds input values to Adiak from parsed arguments
54+
"""
55+
if args.verbose:
56+
if (args.caliperConfig):
57+
print(" * caliperConfig = ", args.caliperConfig)
58+
if (args.caliperFilename):
59+
print(" * caliperFilename = ", ars.caliperFilename)
60+
if (args.caliperConfigJSON):
61+
print(" * caliperConfigJSON = ", args.caliperConfigJSON)
62+
if (not TimerMgr.timers_usable()):
63+
if (args.caliperConfig or args.caliperFilename or args.caliperConfigJSON):
64+
print("WARNING: Caliper command line inputs provided for "+\
65+
"non-timer install. Reconfigure the install with "+\
66+
"-DENABLE_TIMER=ON to be able to use Caliper timers.")
67+
if(args.caliperConfigJSON):
68+
TimerMgr.load(args.caliperConfigJSON)
69+
if(not args.caliperConfig):
70+
raise RuntimeError("SpheralOptionParser: specifying a configuration file without "+\
71+
"using one of the configurations means no timers are started")
72+
off_tests = ["none", "off", "disable", "disabled", "0"]
73+
# Check if Caliper is turned off
74+
if (args.caliperConfig):
75+
if (args.caliperConfig.lower() in off_tests):
76+
return
77+
TimerMgr.add(args.caliperConfig)
78+
TimerMgr.start()
79+
else:
80+
import os, sys
81+
# If output name for Caliper is given, use it
82+
if (args.caliperFilename):
83+
testname = args.caliperFilename
84+
else:
85+
from datetime import datetime
86+
# Append the current day and time to the filename
87+
unique_digits = datetime.now().strftime("_%Y_%m_%d_%H%M%S_%f")
88+
# Name file based on name of python file being run
89+
testname = os.path.splitext(os.path.basename(sys.argv[0]))[0]
90+
testname += unique_digits + ".cali"
91+
TimerMgr.default_start(testname)
92+
# Add number of ranks and threads per rank
93+
adiak_value("threads_per_rank", SpheralOpenMP.omp_get_num_threads())
94+
adiak_value("num_ranks", mpi.procs)
95+
96+
# Add --adiakData inputs as Adiak metadata
97+
if (args.adiakData):
98+
for key, val in args.adiakData.items():
99+
adiak_value(key, val)
100+
return

tests/functional/Damage/TensileDisk/TensileDisk-2d.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
plotFlaws = False,
7676
clearDirectories = False,
7777
dataDirBase = "dumps-TensileDisk-2d",
78-
outputFile = "None",
78+
outputFile = None,
7979

8080
# Should we restart (-1 => find most advanced available restart)
8181
restoreCycle = -1,

tests/functional/Damage/TensileRod/TensileRod-1d.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ def restoreState(self, file, path):
171171
clearDirectories = False,
172172
referenceFile = "Reference/TensileRod-GradyKippOwen-1d-1proc-reproducing-20240816.gnu",
173173
dataDirBase = "dumps-TensileRod-1d",
174-
outputFile = "None",
175-
comparisonFile = "None",
174+
outputFile = None,
175+
comparisonFile = None,
176176
)
177177

178178
# On the IBM BlueOS machines we have some tolerance issues...
@@ -744,7 +744,7 @@ def restoreState(self, file, path):
744744
#-------------------------------------------------------------------------------
745745
# If requested, write out the state in a global ordering to a file.
746746
#-------------------------------------------------------------------------------
747-
if outputFile != "None":
747+
if outputFile:
748748
from SpheralTestUtilities import multiSort
749749
state = State(db, integrator.physicsPackages())
750750
outputFile = os.path.join(dataDir, outputFile)
@@ -786,7 +786,7 @@ def restoreState(self, file, path):
786786
# Also we can optionally compare the current results with another file for
787787
# bit level consistency.
788788
#---------------------------------------------------------------------------
789-
if comparisonFile != "None" and BuildData.cxx_compiler_id != "IntelLLVM":
789+
if comparisonFile and BuildData.cxx_compiler_id != "IntelLLVM":
790790
comparisonFile = os.path.join(dataDir, comparisonFile)
791791
import filecmp
792792
assert filecmp.cmp(outputFile, comparisonFile)

tests/functional/Damage/TensileRod/TensileRod-2d.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def restoreState(self, file, path):
166166

167167
clearDirectories = False,
168168
dataDirBase = "dumps-TensileRod-2d",
169-
outputFile = "None",
169+
outputFile = None,
170170
)
171171

172172
dx = xlength/nx

tests/functional/Gravity/CollisionlessSphereCollapse.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@
263263
#-------------------------------------------------------------------------------
264264
# If requested, write out the profiles
265265
#-------------------------------------------------------------------------------
266-
if outputFile != "None" and mpi.rank == 0:
266+
if outputFile and mpi.rank == 0:
267267
outputFile = os.path.join(dataDir, outputFile)
268268
f = open(outputFile, "w")
269269
f.write(("# " + 14*"%15s " + "\n") % ("r", "x", "y", "z", "vx", "vy", "vz", "Hxx", "Hxy", "Hxz", "Hyy", "Hyz", "Hzz", "phi"))

0 commit comments

Comments
 (0)