From 1a8893fc457e265ec86e3f4abc7547dc2c0d3aca Mon Sep 17 00:00:00 2001 From: Rudolf Weeber Date: Sat, 13 Jun 2026 17:11:11 +0200 Subject: [PATCH] magnetostatics: guard DipolarDirectSum gpu=True without CUDA (bug-sweep #31) DipolarDirectSum::do_construct forwarded the gpu flag straight into the core actor (m_is_gpu=true) with no feature check, unlike the analogous CoulombP3M path. On a CUDA-off build this constructed and activated silently; the first force/energy call then either hit assert(not m_is_gpu) (debug) or silently computed on CPU while is_gpu() reported True (release) -- a capability misreport. Mirror the CoulombP3M convention: at the script-interface seam, if gpu=True, add 'CUDA' to required_features and call CodeInfo::check_features inside parallel_try_catch, so the unsupported request surfaces as a clean RuntimeError ("Missing features CUDA") before the core actor is created. Co-Authored-By: Claude Opus 4.8 --- .../magnetostatics/DipolarDirectSum.hpp | 13 ++++++++++--- testsuite/python/dipolar_interface.py | 6 ++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/script_interface/magnetostatics/DipolarDirectSum.hpp b/src/script_interface/magnetostatics/DipolarDirectSum.hpp index 2a2c409880..df37087d11 100644 --- a/src/script_interface/magnetostatics/DipolarDirectSum.hpp +++ b/src/script_interface/magnetostatics/DipolarDirectSum.hpp @@ -27,10 +27,12 @@ #include "core/magnetostatics/dipolar_direct_sum.hpp" +#include "script_interface/code_info/CodeInfo.hpp" #include "script_interface/get_value.hpp" #include #include +#include namespace ScriptInterface { namespace Dipoles { @@ -47,11 +49,16 @@ class DipolarDirectSum : public Actor { } void do_construct(VariantMap const ¶ms) override { - context()->parallel_try_catch([this, ¶ms]() { + auto const gpu = get_value_or(params, "gpu", false); + context()->parallel_try_catch([this, ¶ms, gpu]() { + if (gpu) { + std::vector required_features; + required_features.emplace_back("CUDA"); + CodeInfo::check_features(required_features); + } m_actor = std::make_shared( get_value(params, "prefactor"), - get_value(params, "n_replicas"), - get_value_or(params, "gpu", false)); + get_value(params, "n_replicas"), gpu); }); } }; diff --git a/testsuite/python/dipolar_interface.py b/testsuite/python/dipolar_interface.py index 22cc14e905..8e83f168a0 100644 --- a/testsuite/python/dipolar_interface.py +++ b/testsuite/python/dipolar_interface.py @@ -90,6 +90,12 @@ def test_exceptions_non_p3m(self): MDLC(gap_size=2., maxPWerror=0.1, actor=ddsr_gpu) with self.assertRaisesRegex(ValueError, "Parameter 'n_replicas' must be >= 0"): DDSR(prefactor=1., n_replicas=-2, gpu=True) + else: + # requesting the GPU implementation without CUDA support compiled + # in must raise a clear error at construction time, rather than + # silently constructing a CPU actor that reports is_gpu() == True + with self.assertRaisesRegex(RuntimeError, "Missing features CUDA"): + DDSR(prefactor=1., gpu=True) with self.assertRaisesRegex(RuntimeError, "Parameter 'actor' is missing"): MDLC(gap_size=2., maxPWerror=0.1) with self.assertRaisesRegex(RuntimeError, "Parameter 'n_replica' is not a valid parameter"):