4.3.1. Writing and Reading a Steady-State Restart

This tutorial writes a steady-state transport solution to a restart file and loads it into a new, compatible problem.

4.3.1.1. Define a repeatable problem

Restart data are compatible only when the MPI rank count, mesh, energy groups, and discretization match. A helper function ensures that the writing and reading problems use identical definitions.

[ ]:
from pathlib import Path

from mpi4py import MPI
from pyopensn.aquad import GLProductQuadrature1DSlab
from pyopensn.context import Finalize
from pyopensn.mesh import OrthogonalMeshGenerator
from pyopensn.post import VolumePostprocessor
from pyopensn.solver import DiscreteOrdinatesProblem, SteadyStateSourceSolver
from pyopensn.source import VolumetricSource
from pyopensn.xs import MultiGroupXS

comm = MPI.COMM_WORLD
rank = comm.rank
restart_stem = Path("tutorial_output/steady_state")

def build_problem(options):
    nodes = [2.0 * i / 40.0 for i in range(41)]
    mesh = OrthogonalMeshGenerator(node_sets=[nodes]).Execute()
    mesh.SetUniformBlockID(0)
    xs = MultiGroupXS()
    xs.CreateSimpleOneGroup(sigma_t=1.0, c=0.5)
    source = VolumetricSource(block_ids=[0], group_strength=[1.0])
    quadrature = GLProductQuadrature1DSlab(n_polar=32, scattering_order=0)
    problem = DiscreteOrdinatesProblem(
        mesh=mesh,
        num_groups=1,
        groupsets=[
            {
                "groups_from_to": (0, 0),
                "angular_quadrature": quadrature,
                "inner_linear_method": "petsc_gmres",
                "l_abs_tol": 1.0e-10,
            }
        ],
        xs_map=[{"block_ids": [0], "xs": xs}],
        volumetric_sources=[source],
        options=options,
    )
    return problem, SteadyStateSourceSolver(problem=problem)

def average_flux(problem):
    postprocessor = VolumePostprocessor(problem=problem, value_type="avg")
    postprocessor.Execute()
    return float(postprocessor.GetValue()[0][0])

4.3.1.2. Write the restart

For a steady-state solver, enabling restart writes produces a final dump after Execute completes. OpenSn appends the MPI rank and .restart.h5 to the supplied path stem.

[ ]:
write_problem, write_solver = build_problem(
    {
        "restart_writes_enabled": True,
        "write_restart_path": str(restart_stem),
    }
)
write_solver.Initialize()
write_solver.Execute()
reference_average = average_flux(write_problem)
comm.Barrier()

4.3.1.3. Read the restart

A second problem reads the saved state during Initialize. We inspect the loaded scalar flux before executing another transport solve, proving that the restart—not a repeated solve—restored the state.

[ ]:
read_problem, read_solver = build_problem({"read_restart_path": str(restart_stem)})
read_solver.Initialize()
restarted_average = average_flux(read_problem)
difference = abs(reference_average - restarted_average)
if rank == 0:
    print(f"Written average flux={reference_average:.6e}")
    print(f"Restarted average flux={restarted_average:.6e}")
    print(f"Restart flux difference={difference:.6e}")
assert difference < 1.0e-12

comm.Barrier()
restart_file = Path(f"{restart_stem}{rank}.restart.h5")
restart_file.unlink(missing_ok=True)
comm.Barrier()
if rank == 0:
    restart_stem.parent.rmdir()
if "opensn_console" not in globals():
    from IPython import get_ipython
    if get_ipython() is not None:
        Finalize()
        MPI.Finalize()