Repository navigation
Description
Came up while reviewing #71, which added a test that selects a backend. Not a problem with that PR — the new test follows the existing convention, and the gap is in the convention. If anything the new test degrades more gracefully than the older ones; see below.
SBD_BUILD_BACKEND=gpu and SBD_BUILD_BACKEND=gpu_omp_offload build no CPU backend — setup.py leaves build_cpu at False in both branches, and INSTALL.md documents them ("gpu — Thrust GPU only, no CPU"). On such a build, parts of the test suite fail rather than skip unless SBD_TEST_DEVICE is set.
The cause is that the package default device is cpu, not auto. init(device='cpu') is the signature default, and _ensure_initialized() calls bare init(), which raises when cpu is not in _scan_backends():
RuntimeError: Device 'cpu' requested but its backend is not usable
(not built (no _core_cpu.*.so in the package directory)). Usable: ['gpu-omp']
'auto' would resolve correctly — _resolve_device falls through to gpu, then gpu-omp, then cpu — but nothing defaults to it.
I built SBD_BUILD_BACKEND=gpu_omp_offload (only _core_gpu_omp_offload.so present, available_backends() → ['gpu-omp']) and ran the suite with SBD_TEST_DEVICE unset. Two places reach that path, and they fail differently:
test/conftest.py:111, thebackendfixture:sbd.init(device=device or sbd.get_device())._requested_device()returnsNonewhen the variable is unset, andget_device()auto-initializes tocpu. Because this raises inside a session-scoped fixture, the pytest process dies during collection — no traceback, no summary, exit 1. Ontest_sci_state.pythe output stops after the collected-items line. Consumers includetest_sci_state.py,test_reference_energies.py,test_gdb_equivalence.pyandtest_sqd_integration.py.test/test_wide_orbitals.py,_device(): returns"cpu"when the variable is unset, which the subprocess then passes tosbd.init. This reports a normal1 failedwith a readable assertion, since the error is captured from a subprocess.
Unset is the normal case, not an unusual one. tox.ini lists SBD_TEST_DEVICE in passenv only, with no setenv default (unlike SBD_TEST_NPROCS, which gets {env:SBD_TEST_NPROCS:2}), and none of the CI workflows set it. conftest._requested_device() documents None as meaning "whatever the build defaults to", and its not-built skip is deliberately conditional on the variable being set:
if device is not None and device not in available:
pytest.skip(f"backend {device!r} was not built; available: {available}")So a backend named explicitly is skipped when missing, but the implicit cpu is not checked at all.
Setting SBD_TEST_DEVICE=gpu-omp is an effective workaround, so this only affects someone running the suite against a GPU-only build without it.
Possible directions, in rough order of how much they change:
- Have
_requested_device()also skip when the variable is unset andcpuis not built. Smallest change, keeps everything else as-is, and fixes both sites at once.sbd.available_backends()only globs for.sofiles, so it can be called without loading a backend. - Fall back to
'auto'rather thancpuwhen the variable is unset. Matches what a CPU-less build presumably wants, but changes which backend a multi-backend build tests by default, which thebackendfixture's comment warns about. - Leave the behaviour and document that a GPU-only build needs
SBD_TEST_DEVICE. The error itself is already specific — it names the device, the missing.soand what is usable — but it says "Device 'cpu' requested", and in this case nobody requested it; it came frominit's default. Distinguishing the two, e.g. "no device was requested and the default 'cpu' is not usable", would point at the fix rather than at a request the user never made. On its own this would still leave the silent collection-time death in direction 1's first bullet.
Environment for the above: Fedora 44, NVHPC 26.9, SBD_GPU_ARCH=cc89, built against NVHPC's HPC-X OpenMPI 5.0.10.
This issue was drafted by Claude Opus 5 under my guidance.
Activity