Skip to content

Commit

Permalink
Merge pull request #22 from tlranda/permit_nans
Browse files Browse the repository at this point in the history
OpenMC: Permit Integer nans to exist when value is not sampled
  • Loading branch information
wuxf99 authored Jul 6, 2023
2 parents 013dcc5 + c753c6f commit 5b9173e
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 27 deletions.
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Repository artifacts that do not need to be tracked
*.job
*.log
tmp_files/
tmpoutfile.txt


# Python artifacts that do not need to be tracked
__pycache__/
*.py[cod]
*$py.class
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.install.cfg
*.egg
MANIFEST
*.manifest
*.spec
pip-log.txt
pip-delete-this-directory.txt
.ipynb_checkpoints
cython_debug/

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
27 changes: 17 additions & 10 deletions ytopt-libe-openmc/run_ytopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,6 @@
#libE_specs['sim_dir_copy_files'] = [here + f for f in ['mmp.c', 'Materials.c', 'XSutils.c', 'XSbench_header.h']]
libE_specs['sim_dir_symlink_files'] = [here + f for f in ['openmc.sh', 'settings.xml', 'materials.xml', 'geometry.xml', 'exe.pl', 'plopper.py', 'processexe.pl']]

# Declare the sim_f to be optimized, and the input/outputs
sim_specs = {
'sim_f': init_obj,
'in': ['p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'],
'out': [('RUNTIME', float),('elapsed_sec', float)],
}

cs = CS.ConfigurationSpace(seed=1234)
# queuing logic type
p0 = CSH.CategoricalHyperparameter(name='p0', choices=["openmc", "openmc-queueless"], default_value="openmc-queueless")
Expand All @@ -84,8 +77,8 @@
p6= CSH.CategoricalHyperparameter(name='p6', choices=['cores','threads','sockets'], default_value='threads')

cs.add_hyperparameters([p0, p1, p2, p3, p4, p5, p6])
#cond = EqualsCondition(p3, p0, "openmc")
#cs.add_conditions([cond])
cond = EqualsCondition(p3, p0, "openmc")
cs.add_conditions([cond])

ytoptimizer = Optimizer(
num_workers=num_sim_workers,
Expand All @@ -98,11 +91,24 @@
set_NI=10,
)

# Declare the sim_f to be optimized, and the input/outputs
# Add p3_present indicator due to LibEnsemble API -- it is implicit from the ConfigSpace
# but must be explicitly tracked due to current limitations.
sim_specs = {
'sim_f': init_obj,
'in': ['p0', 'p1', 'p2',
'p3_present',
'p3', 'p4', 'p5', 'p6'],
'out': [('RUNTIME', float),('elapsed_sec', float)],
}

# Declare the gen_f that will generate points for the sim_f, and the various input/outputs
gen_specs = {
'gen_f': persistent_ytopt,
'out': [('p0', "<U24", (1,)), ('p1', int, (1,)), ('p2', int, (1,)),
('p3', int, (1,)), ('p4', int, (1,)), ('p5', int, (1,)), ('p6', "<U24", (1,))],
('p3_present', bool, (1,)),
('p3', int, (1,)),
('p4', int, (1,)), ('p5', int, (1,)), ('p6', "<U24", (1,))],
'persis_in': sim_specs['in'] + ['RUNTIME'] + ['elapsed_sec'],
'user': {
'ytoptimizer': ytoptimizer, # provide optimizer to generator function
Expand Down Expand Up @@ -138,3 +144,4 @@
#b = np.vstack(map(list, H[gen_specs['persis_in']]))
#print(b)
#np.savetxt('results.csv',b, header=','.join(dtypes.names), delimiter=',',fmt=','.join(['%s']*b.shape[1]))

73 changes: 60 additions & 13 deletions ytopt-libe-openmc/ytopt_asktell.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ def persistent_ytopt(H, persis_info, gen_specs, libE_info):
first_call = True
first_write = True
fields = [i[0] for i in gen_specs['out']]
# The order of fields must semantically match what Ytopt will see,
# else Ytopt will fail to match any key - eval pairings.
#
# Due to brittleness in libEnsemble interactions, reordering
# this 'sometimes-present' field to the back minimizes changes.
# In the future, enforced key ordering for Ytopt's evaluation keys
# should eliminate need for this adjustment.
fields.pop(fields.index('p3'))
fields.append('p3')

# Send batches until manager sends stop tag
while tag not in [STOP_TAG, PERSIS_STOP]:
Expand All @@ -34,40 +43,78 @@ def persistent_ytopt(H, persis_info, gen_specs, libE_info):
for entry in calc_in:
field_params = {}
for field in fields:
field_params[field] = entry[field][0]
# Presence fields are NOT passed to Ytopt.
# However, they must be provided to LibEnsemble due to API semantics.
# We represent 'not-present' values with np.nan for scikit-optimize.
if "present" in field:
continue
elif field+"_present" in fields:
# This is the from->LibEnsemble sentinel adjustment
if not entry[fields.index(field+"_present")]:
field_params[field] = np.nan
continue
else:
if field not in entry.dtype.names:
continue
field_params[field] = float(entry[field][0])
else:
if field not in entry.dtype.names:
continue
field_params[field] = entry[field][0]
results += [(field_params, entry['RUNTIME'])]
print('results: ', results)
ytoptimizer.tell(results)

ytopt_points = ytoptimizer.ask(n_points=batch_size) # Returns a generator that we convert to a list
ytopt_points = list(ytopt_points)[0]

# We must evaluate the presence here, as LibEnsemble will not permit us to assing
# np.nan as an integer parameter value.
# We'll use -1 (invalid from ConfigSpace POV) as a sentinel, and restore the nan-ness after
# LibEnsemble has done its handoff.
# This presence field exists to indicate that such restoration will occur. The sentinel
# does not need to be invalid for the ConfigSpace, but doing so helps validate that this
# wrapper intercepts its own meddling before propagating it to components that should not
# be affected.
for point in ytopt_points:
point['p3_present'] = not np.isnan(point['p3'])
# The hand-off of information from ytopt to libE is below. This hand-off may be brittle.
H_o = np.zeros(batch_size, dtype=gen_specs['out'])
for i, entry in enumerate(ytopt_points):
for key, value in entry.items():
H_o[i][key] = value
# This is the to->LibEnsemble sentinel adjustment.
# String data and many other representations cannot be checked by np.isnan.
# If the data isn't numerical, we're not making a nan-based adjustment so we
# just assign the original value.
try:
if np.isnan(value):
H_o[i][key] = -1
else:
H_o[i][key] = value
except:
H_o[i][key] = value

# This returns the requested points to the libE manager, which will
# perform the sim_f evaluations and then give back the values.
tag, Work, calc_in = ps.send_recv(H_o)
print('received:', calc_in, flush=True)

if calc_in is not None:
if len(calc_in):
b = []
for entry in calc_in[0]:
try:
if calc_in is not None and len(calc_in):
b = []
for calc_result in calc_in:
for entry in calc_result:
# Most entries should be 1-length np.ndarrays, however if the top-level implementer
# failed to indicate to LibEnsemble that this would be the case, they'll come back
# as just the value.
try:
b += [str(entry[0])]
except:
except:
b += [str(entry)]

with open('../../results.csv', 'a') as f:
if first_write:
f.write(",".join(calc_in.dtype.names)+ "\n")
f.write(",".join(b)+ "\n")
f.write(",".join(calc_result.dtype.names)+ "\n")
first_write = False
else:
f.write(",".join(b)+ "\n")
f.write(",".join(b)+ "\n")

return H_o, persis_info, FINISHED_PERSISTENT_GEN_TAG

5 changes: 1 addition & 4 deletions ytopt-libe-openmc/ytopt_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@ def plopper_func(x, params):
obj = Plopper('./openmc.sh', './')
x = np.asarray_chkfinite(x)
value = [point[param] for param in params]
#print(value)
os.system("./processexe.pl exe.pl " +str(value[4])+ " " +str(value[5])+ " " +str(value[6]))
os.environ["OMP_NUM_THREADS"] = str(value[4])
params = [i.upper() for i in params]
#result = float(obj.findRuntime(value, params, workerID))
result = obj.findRuntime(value, params, workerID)
return result

x = np.array([point[f'p{i}'] for i in range(len(point))])
x = np.array(list(point.values()))
results = plopper_func(x, params)
# print('CONFIG and OUTPUT', [point, results], flush=True)
return results

0 comments on commit 5b9173e

Please sign in to comment.