Over the summer with python 3.11.11 and this commit hash af35184871abbc4d38d5d18dcae1718aca721800, I was able to do criticality searches with the following code (named critical_search.py)
import openmc
import numpy as np
import matplotlib.pyplot as plt
from argparse import ArgumentParser
r_inner = 10.0
r_outer = 20.0
height = 100.0
def build_model(rotation):
model = openmc.Model()
# materials
fuel = openmc.Material(name="fuel")
fuel.add_element("U", 1.0, enrichment=19.95)
fuel.set_density("g/cm3", 17.3)
water = openmc.Material(name="water")
water.add_elements_from_formula("H2O")
water.set_density("g/cm3", 1.0)
model.materials = openmc.Materials([fuel, water])
# geometry
z_min = openmc.ZPlane(z0=0.0, boundary_type="vacuum")
z_max = openmc.ZPlane(z0=specs.height, boundary_type="vacuum")
x_divider = openmc.XPlane(x0=0.0) # at rotation=zero
cyl_inner = openmc.ZCylinder(r=specs.r_inner)
cyl_outer = openmc.ZCylinder(r=specs.r_outer, boundary_type="vacuum")
inner_fuel_cell = openmc.Cell(
region=+z_min & -z_max & -x_divider & -cyl_inner, fill=fuel
)
inner_mod_cell = openmc.Cell(
region=+z_min & -z_max & +x_divider & -cyl_inner, fill=water
)
outer_fuel_cell = openmc.Cell(
region=+z_min & -z_max & -x_divider & +cyl_inner & -cyl_outer, fill=fuel
)
outer_mod_cell = openmc.Cell(
region=+z_min & -z_max & +x_divider & +cyl_inner & -cyl_outer, fill=water
)
inner_cyl_univ = openmc.Universe(cells=[inner_fuel_cell, inner_mod_cell])
inner_cyl_cell = openmc.Cell(region=-cyl_inner & +z_min & -z_max, fill=inner_cyl_univ)
inner_cyl_cell.rotation = (0.0, 0.0, rotation) # rotate about z axis
model.geometry = openmc.Geometry([inner_cyl_cell, outer_fuel_cell, outer_mod_cell])
# build settings
settings = openmc.Settings()
settings.batches = 300
settings.inactive = 100
settings.particles = 500000
# Create an initial uniform spatial source distribution over the whole geometry, the first generation will fix it to be only fissionable sites
radii = openmc.stats.Uniform(0.0, specs.r_outer)
angles = openmc.stats.Uniform(0, 2 * np.pi)
heights = openmc.stats.Uniform(0.0, specs.height)
cylindrical_source = openmc.stats.CylindricalIndependent(r=radii, phi=angles, z=heights)
settings.source = openmc.IndependentSource(space=cylindrical_source)
settings.output = {"summary": False}
settings.temperature = {
"default": 294.0,
"method": "nearest",
"range": (294.0, 1600.0),
}
model.settings = settings
return model
def main(a, b, tol, run_args):
# do search
procs = run_args["mpi_args"][-1]
print(run_args)
bracket = [a, b]
crit_angle, guesses, keffs = openmc.search_for_keff(
build_model,
bracketed_method="brentq",
bracket=bracket,
tol=tol,
print_iterations=True,
run_args=run_args
)
print(
f"The critical angle achieved with a tolerance of {tol} was {crit_angle} using {procs} mpi procs"
)
ks = [keffs[i].nominal_value for i in range(len(keffs))]
sig_ks = [keffs[i].std_dev for i in range(len(keffs))]
with open("guesses.txt", "w") as guess_file:
for iteration, (guess, k, sig) in enumerate(zip(guesses, ks, sig_ks)):
guess_file.write(
f"Iteration {iteration} had {guess} degrees resulting in {k}+/-{sig}\n"
)
guess_file.write(
f"The search resulted in a criticial guess of {crit_angle} using {procs} mpi procs and tolerance={tol}"
)
def build_parser():
ap = ArgumentParser()
ap.add_argument(
"-np",
dest="mpi_procs",
type=int,
default="1",
help="The number of MPI processes to run",
)
ap.add_argument(
"--tol",
dest="tol",
type=float,
default=0.001,
help="The tolerance to use in the critical search",
)
ap.add_argument(
"-a",
dest="a",
type=float,
default=0.0,
help="The bracket lower bound, inclusive, (rotation in degrees) for the search",
)
ap.add_argument(
"-b",
dest="b",
type=float,
default=90.0,
help="The bracket upper bound, inclusive, (rotation in degrees) for the search",
)
ap.add_argument(
"--print",
dest="print",
action="store_true",
help="when passed, print the k-eigenvalue solves",
)
return ap.parse_args()
if __name__ == "__main__":
args = build_parser()
run_args = {"mpi_args": ["mpiexec", "-n", f"{args.mpi_procs}"], "threads": "1"}
if not args.print:
run_args["output"] = False
main(args.a, args.b, args.tol, run_args)
I’ve revisited this code with a newer OpenMC (using this commit 5847b0de23eaf152cb5078801ee13cad2d2cc015) and python 3.13.1. When I try to run a search via python critical_search.py --print -np 24, I’m getting a strange error
Traceback (most recent call last):
File "/home/lgross/test_crit_search_for_PR/make_openmc_model.py", line 173, in <module>
main(args.a, args.b, args.tol, run_args)
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/lgross/test_crit_search_for_PR/make_openmc_model.py", line 73, in main
crit_angle, guesses, keffs = openmc.search_for_keff(
~~~~~~~~~~~~~~~~~~~~~~^
build_model,
^^^^^^^^^^^^
...<4 lines>...
run_args=run_args
^^^^^^^^^^^^^^^^^
)
^
File "/home/lgross/openmc/openmc/search.py", line 202, in search_for_keff
zero_value = root_finder(**args)
File "/home/lgross/.pyenv/versions/3.13.1/lib/python3.13/site-packages/scipy/optimize/_zeros_py.py", line 798, in brentq
r = _zeros._brentq(f, a, b, xtol, rtol, maxiter, args, full_output, disp)
File "/home/lgross/.pyenv/versions/3.13.1/lib/python3.13/site-packages/scipy/optimize/_zeros_py.py", line 94, in f_raise
fx = f(x, *args)
File "/home/lgross/openmc/openmc/search.py", line 54, in _search_keff
sp_filepath = model.run(**run_args)
File "/home/lgross/openmc/openmc/model/model.py", line 882, in run
openmc.run(particles, threads, geometry_debug, restart_file,
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tracks, output, Path('.'), openmc_exec, mpi_args,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
event_based, path_input)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/lgross/openmc/openmc/executor.py", line 314, in run
_run(args, output, cwd)
~~~~^^^^^^^^^^^^^^^^^^^
File "/home/lgross/openmc/openmc/executor.py", line 125, in _run
raise RuntimeError(error_msg)
RuntimeError: OpenMC aborted unexpectedly.
I’ve tried a few debugging tactics and have narrowed it down a bit. The first thing I tried was supplying an empty dictionary {} to run_args in the search method, which allows the criticality search to start with 1 MPI proc and 1 OpenMP thread. I can also supply run_args = {"mpi_args":[]} without the error.
As soon as I specify anything in the value of the "mpi_args" key, i.e. something like ["mpiexec","-n","24"], the code complains. I added a print statement into executor.py right before the _run(args, output, cwd) to just print(args) and what prints is ['mpiexec', '-n', '24', 'openmc']. As I understand it, this means that OpenMC is trying to run the command mpiexec -n 24 openmc. Strangely, outside this script in the terminal, I can run mpiexec -n 24 openmc with my local model.xml present and it runs just fine with the correct number of MPI procs.
I was wondering if there was some issue with multiple python/multiple OpenMC installs, so I just used pyenv uninstall to destroy all non-python 3.13.1 executables. I’m still getting the issue, though.
Does anyone have any clue what is going on? Would be great to know if someone can try my code on their machine (even better if it 's python 3.13.1 and the same commit hash) to see if it’s some local install issue of mine or if the problem duplicated elsewhere. All we really need is to know if the error message is the same, different, or the criticality search just starts with the correct mpi_args.