Skip to content

Model.init_lib raises AttributeError when directory is a pathlib.Path #4185

Closed

Description

@ondrejch

Bug Description

Model.init_lib() documents its directory argument as PathLike or None (openmc/model/model.py:485; the annotation at :456 is directory: PathLike | None, with PathLike = str | os.PathLike from openmc/checkvalue.py:9). With a str it works. With a pathlib.Path it writes the XML files to the directory and then fails in openmc.lib.init() with AttributeError: 'PosixPath' object has no attribute 'encode'.

Line numbers are at a5bc348; the cited files are the same on the current develop (8b12115).

  • init_lib() passes directory to _process_CLI_arguments() as path_input=directory (openmc/model/model.py:497-500). It then exports the XML files to directory on rank 0 (:514-517) and calls openmc.lib.init(args=args, ...) (:524).
  • _process_CLI_arguments() documents path_input : str or PathLike and returns "Iterable of str" (openmc/executor.py:46, :54). It converts restart_file with str() (:77-78), but appends path_input as given: args += [path_input] (:89-90).
  • openmc.lib.init() documents args : list of str and calls create_string_buffer(arg.encode()) on every argument (openmc/lib/core.py:322, :339-340). A Path has no encode(), so it raises AttributeError.

openmc.run() and openmc.calculate_volumes() use the same _process_CLI_arguments(), but they pass the list to subprocess.Popen (openmc/executor.py:97), which accepts path-like arguments. Only the in-memory initialization fails.

restart_file has a related problem: _process_CLI_arguments() converts it with str() (openmc/executor.py:77-78), and str() of an os.PathLike that is not a pathlib path, such as an os.DirEntry or a class that defines only __fspath__(), is its repr, so openmc.run(), Model.run() and Model.init_lib() pass, for example, -r "<DirEntry 'restart.h5'>". Converting path_input with str() in the same way would bring that problem to directory as well.

Expected: init_lib(directory=pathlib.Path(...)) initializes OpenMC from the XML files in that directory, as it does with a str.

The directory argument was added in #3374 and annotated PathLike in #3399. Reading the code shows the same behaviour in v0.15.3 and v0.16.0; nothing was run on those versions.

A pull request with a proposed fix will follow.

Steps to Reproduce

The script below needs no nuclear data: it writes its own one-group multigroup library. It initializes the same model twice, first with a str directory and then with a pathlib.Path directory. Run it in an empty directory:

"""Model.init_lib accepts a str directory but not a pathlib.Path one.

Run in an empty directory. Needs no nuclear data (one-group multigroup
library written by the script).
"""
import os
import pathlib

import openmc
import openmc.lib

groups = openmc.mgxs.EnergyGroups([0.0, 20.0e6])
xsdata = openmc.XSdata('medium', groups)
xsdata.order = 0
xsdata.set_total([1.0])
xsdata.set_absorption([0.25])
xsdata.set_scatter_matrix([[[0.75]]])
xsdata.set_inverse_velocity([1.0e-5])
library = openmc.MGXSLibrary(groups)
library.add_xsdata(xsdata)
library.export_to_hdf5('one_group.h5')

medium = openmc.Material()
medium.set_density('macro', 1.0)
medium.add_macroscopic('medium')

model = openmc.Model()
model.materials = openmc.Materials([medium])
model.materials.cross_sections = os.path.abspath('one_group.h5')
box = openmc.model.RectangularParallelepiped(
    -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, boundary_type='reflective')
model.geometry = openmc.Geometry([openmc.Cell(fill=medium, region=-box)])
model.settings.energy_mode = 'multi-group'
model.settings.run_mode = 'fixed source'
model.settings.batches = 2
model.settings.particles = 100
model.settings.source = openmc.IndependentSource(
    space=openmc.stats.Point(),
    energy=openmc.stats.Discrete([1.0e6], [1.0]))

# A str directory works
model.init_lib(output=False, directory='sub_str')
print('directory=str: openmc.lib.is_initialized =', openmc.lib.is_initialized)
model.finalize_lib()
print('files in sub_str:', sorted(os.listdir('sub_str')))

# A pathlib.Path directory fails after the XML files have been written
try:
    model.init_lib(output=False, directory=pathlib.Path('sub_path'))
finally:
    print('files in sub_path:', sorted(os.listdir('sub_path')))

Output on develop (a5bc348), local paths shortened to ...:

directory=str: openmc.lib.is_initialized = True
files in sub_str: ['geometry.xml', 'materials.xml', 'settings.xml']
files in sub_path: ['geometry.xml', 'materials.xml', 'settings.xml']
Traceback (most recent call last):
  File ".../repro_init_lib_path.py", line 49, in <module>
    model.init_lib(output=False, directory=pathlib.Path('sub_path'))
  File ".../openmc/model/model.py", line 524, in init_lib
    openmc.lib.init(args=args, intracomm=intracomm, output=output)
  File ".../openmc/lib/core.py", line 340, in init
    argv[i] = create_string_buffer(arg.encode())
                                   ^^^^^^^^^^
AttributeError: 'PosixPath' object has no attribute 'encode'

The XML files are already in sub_path when the error is raised (the finally block lists them), so the export works and the failure is in openmc.lib.init().

Environment

  • OpenMC develop at a5bc348 (openmc --version: 0.16.1-dev57), built from source with CMake: RelWithDebInfo, -DOPENMC_ENABLE_STRICT_FP=on, OpenMP on, MPI off, HDF5 1.10.10 (serial).
  • Ubuntu 24.04 (x86_64), GCC 13.3.0, Python 3.12.3 with numpy 2.5.3 and h5py 3.16.0; OMP_NUM_THREADS=2.
  • Nuclear data: none; the script writes its own multigroup library.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions