# scikit-build-core builds the .so via CMake; the debug sources are not tracked # into debugsourcefiles.list (empty -> FTBFS), so disable the debug package. %global debug_package %{nil} # Upstream tags irregularly (v1.2.7 is 2026-07; main has fixes since); pin # the snapshot instead of fetching a moving main.tar.gz. %global commit 61467ad4278d903bdcad678bbb797bad5537884e %global shortcommit %(c=%{commit}; echo ${c:0:8}) %global snapdate 20260806 Name: python-babelviscofdtd Version: 1.2.7 Release: 4.%{snapdate}git%{shortcommit}%{?dist} Summary: GPU/CPU 3D FDTD solution of viscoelastic equation License: BSD-3-Clause URL: https://github.com/ProteusMRIgHIFU/BabelViscoFDTD Source0: %{url}/archive/%{commit}/BabelViscoFDTD-%{shortcommit}.tar.gz # Upstream appends -march=native, which bakes the builder's ISA into the rpm. Patch0: babelviscofdtd-no-march-native.patch # The extensions install inside the package but are imported from top level. Patch1: babelviscofdtd-relative-extension-import.patch # Seven modules (the BASE, OPENCL, METAL and MLX backends) import # distutils.sysconfig.get_python_inc and never use it. distutils is gone in # Python 3.12, so on any system without setuptools the OpenCL backend cannot # be imported, PropagationModel prints "StaggeredFDTD_3D_OPENCL NOT loaded" # and BabelBrain finds no compute device. Drop the import. Patch2: babelviscofdtd-no-distutils.patch # The GPU backends (CUDA, OpenCL, Metal, MLX) write their kernel constants # with "%%.9g" % IP[name][n]; for a (1, N) parameter that is a 1-element # array, which numpy 2.4 refuses to format ("only 0-dimensional arrays can # be converted to Python scalars"). Every OpenCL simulation on f44 (numpy # 2.4.6) died while generating kernel source. Format from scalars. Patch3: babelviscofdtd-numpy24-scalar-format.patch # One plane wave through water, speed of sound recovered from two sensors; # run on the CPU and OpenCL backends in %%check and by tests/babelviscofdtd.bats. Source1: babelviscofdtd-wave-check.py BuildRequires: python3-devel BuildRequires: python3-pip # pyproject.toml uses build-backend = scikit_build_core.build (not setuptools), # which drives cmake via the ninja generator. BuildRequires: python3-scikit-build-core BuildRequires: ninja-build BuildRequires: python3-numpy BuildRequires: python3-scipy BuildRequires: python3-h5py BuildRequires: python3-hdf5plugin BuildRequires: python3-pydicom BuildRequires: python3-pyopencl BuildRequires: cmake BuildRequires: gcc-c++ BuildRequires: opencl-headers BuildRequires: ocl-icd-devel # %%check disassembles the extensions to prove no AVX-512 leaked in BuildRequires: binutils # %%check imports the OpenCL backend and lists a device: pocl on the CPU, # through a pyopencl that can import (Fedora's cannot; this Copr's can). BuildRequires: pocl BuildRequires: python3-pyopencl >= 2026.1 %description GPU/CPU 3D FDTD solution of viscoelastic equation. %package -n python3-babelviscofdtd Summary: %{summary} Requires: python3-numpy Requires: python3-scipy Requires: python3-h5py Requires: python3-hdf5plugin Requires: python3-pydicom Requires: python3-pyopencl >= 2026.1 Recommends: pocl %description -n python3-babelviscofdtd GPU/CPU 3D FDTD solution of viscoelastic equation. %prep %autosetup -n BabelViscoFDTD-%{commit} -p1 %build # setup.py calls cmake. # We ensure cmake and build dependencies are present. %pyproject_wheel %install %pyproject_install %check # Two defects shipped in 1.2.7-1, and the first hid the second. # # 1. The CPU solver -- the only backend this package builds -- could not be # imported at all. StaggeredFDTD_3D_With_Relaxation.py does # `import _FDTDStaggered3D_with_relaxation_double`, an absolute top-level # import, while the extensions install inside BabelViscoFDTD/. The # importing code catches bare `except:` and prints # "StaggeredFDTD_3D_CPU NOT loaded", so the package imported cleanly, # passed every presence check, and had no working solver. _sp=%{buildroot}%{python3_sitearch} # From the source directory, `python3 -c` puts the cwd ahead of PYTHONPATH, # so BabelViscoFDTD resolves to the unbuilt source copy -- which has the .py # files and none of the compiled extensions, and fails with the very error # this check exists to detect. Leave the source tree first. cd / PYTHONPATH="$_sp" %{python3} -c " from BabelViscoFDTD.StaggeredFDTD_3D_With_Relaxation import StaggeredFDTD_3D print('CPU backend imports') " # 2. With the backend loadable it died instantly with SIGILL on a CPU # without AVX-512, because upstream's -march=native let the builder # choose the instruction set. Assert the shipped extensions carry no # AVX-512; %%{optflags} targets the x86_64 baseline and must win. %ifarch x86_64 for _so in $_sp/BabelViscoFDTD/_FDTDStaggered3D_with_relaxation_*.so; do if objdump -d "$_so" | grep -qE '%%zmm[0-9]'; then echo "ERROR: $(basename $_so) contains AVX-512 (%%zmm) -- -march=native leaked in" >&2 exit 1 fi done echo "no AVX-512 in the shipped extensions" %endif # 3. The OpenCL backend (the one BabelBrain uses on Linux without CUDA) must # import and see a device. Until -3 it needed distutils, i.e. setuptools, by # accident, and failed silently on a clean Python 3.12+ install. PYTHONPATH="$_sp" %{python3} -c " from BabelViscoFDTD.StaggeredFDTD_3D_With_Relaxation_OPENCL import ListDevices d = ListDevices(); print('OpenCL devices:', d); assert d, 'pocl gave no device'" # No shipped module may import distutils (removed in Python 3.12). ! grep -rn --include='*.py' distutils "$_sp/BabelViscoFDTD" # 4. A wave actually propagates, on both backends this package can run: the # CPU extension and OpenCL on pocl. The OpenCL run is the one BabelBrain # Step 2 performs on Linux without CUDA; until -4 it died on numpy 2.4 # while generating the kernel source, which no import test can see. # The CPU run is about 25 s on a laptop. pocl is two orders of magnitude # slower than the compiled extension on this kernel (the full box took it # 10.5 min standalone on 16 threads), so the OpenCL run uses the smaller # --quick box (16x16x40 mm; about 70 s here). It cannot go smaller: the # OpenCL backend returns zeros and NaN for an interior of 42 cells or # fewer in x/y, where the CPU backend is fine (reported upstream). PYTHONPATH="$_sp" %{python3} %{SOURCE1} cpu PYTHONPATH="$_sp" %{python3} %{SOURCE1} opencl cpu --quick %files -n python3-babelviscofdtd %license LICENSE %doc README.md # The scikit-build-core backend nests the compiled _FDTDStaggered*.so # extensions inside the package dir (wheel.packages = ["BabelViscoFDTD"]), so # this dir entry captures them; the old top-level .so glob no longer applies. %{python3_sitearch}/BabelViscoFDTD # The dist-info version drifts because Source0 fetches main.tar.gz (not a tag); # the wheel build uses whatever pyproject.toml says in that snapshot. Glob it. %{python3_sitearch}/babelviscofdtd-*.dist-info %changelog * Mon Oct 12 2026 Morgan Hough - 1.2.7-4.20260806git61467ad4 - Patch3: the CUDA, OpenCL, Metal and MLX backends format their kernel constants with "%%.9g" % IP[name][n], a 1-element array for (1, N) parameters. numpy 2.4 makes that a TypeError ("only 0-dimensional arrays can be converted to Python scalars"), so with -3 on f44 every OpenCL simulation, i.e. every BabelBrain Step 2 run on Linux without CUDA, died in _InitSymbolArray before a kernel ran. Found running upstream's analytical propagation test on pocl. Format from scalars (np.ravel / .item()); a no-op on older numpy. - %%check runs a plane wave through water on the CPU and the OpenCL backend and recovers the speed of sound (Source1, shared with tests/babelviscofdtd.bats, which gains the OpenCL case). The -3 check only listed devices; this is the first %%check that would have failed on -3. * Sun Oct 11 2026 Morgan Hough - 1.2.7-3.20260806git61467ad4 - Patch2: drop the unused `from distutils.sysconfig import get_python_inc` from the seven backend modules. distutils does not exist on Python 3.12+, so without setuptools installed the OpenCL backend could not be imported; PropagationModel swallowed that into "StaggeredFDTD_3D_OPENCL NOT loaded" and BabelBrain, which enumerates devices through this module, found none and exited at its first dialog. Found by babelbrain's new %%check in a clean buildroot; hidden on every host that happened to have setuptools. - %%check imports the OpenCL backend and requires a device (pocl, via the pyopencl >= 2026.1 this Copr carries; Fedora's cannot be imported, Bugzilla 2432626). Requires python3-pyopencl >= 2026.1, Recommends pocl. - Source0 pinned to the 2026-08-06 commit instead of a moving main.tar.gz. * Mon Oct 05 2026 Morgan Hough - 1.2.7-2 - The CPU solver, which is the only backend this package builds, has never worked. Two defects, and the first concealed the second. - Patch1: StaggeredFDTD_3D_With_Relaxation.py imports the compiled extensions by their bare top-level names, but scikit-build-core installs them inside BabelViscoFDTD/. The import therefore always failed, and PropagationModel.py catches it with a bare `except:` and prints "StaggeredFDTD_3D_CPU NOT loaded" before carrying on. The package imported, every presence and import test passed, and there was no solver. (It also prints "StaggeredFDTD_3D_METAL loaded" on Linux, which is how little those lines mean.) - Patch0: with the backend loadable, a 48x48x160 water propagation died immediately with SIGILL. Upstream's src/CMakeLists.txt appends -march=native whenever STAGGERED_OPT is set; its only guard is for macOS cross-compilation and never fires on Linux. The COPR builder has AVX-512, so the shipped _FDTDStaggered3D_*.so carried %%zmm registers and could not run on an i9-9980HK (AVX2, no AVX-512) -- or on most consumer x86_64. Removed, leaving %%{optflags} to set the baseline. Same defect class as mrtrix3's ARCH=native in 3.0.8-5. - %%autosetup gains -p1, without which both patches would be applied at strip 0, reported as "ignored", and skipped silently. - %%check now imports the CPU backend and disassembles the shipped extensions to fail the build on any %%zmm register. Either check alone would have caught one of these in May. * Sat Jul 18 2026 Morgan Hough - 1.2.7-1 - Update to 1.2.7 * Wed Jun 10 2026 Morgan Hough - 1.2.6-4 - Fix BackendUnavailable FTBFS: pyproject.toml uses build-backend = scikit_build_core.build, but the spec only BuildRequired setuptools/wheel. BuildRequire python3-scikit-build-core (+ ninja-build, its default cmake generator) so %%pyproject_wheel can load the backend. Unblocks BabelBrain (BabelViscoFDTD is its core solver) * Tue May 26 2026 Morgan Hough - 1.2.6-3 - Restore _FDTDStaggered*.so glob in %%files: the wheel actually installs the cython extensions at BOTH top-level sitearch AND inside BabelViscoFDTD/. The -2 removal was wrong. * Tue May 26 2026 Morgan Hough - 1.2.6-2 - Glob the .dist-info path: Source0 fetches main.tar.gz which drifts between pyproject.toml versions, so a literal version in %%files breaks reproducibility. * Tue May 26 2026 Morgan Hough - 1.2.6-1 - Bump to 1.2.6 (upstream pyproject.toml metadata version; the v1.2.3 GitHub tag actually contains 1.2.6 in setup.cfg). - Drop erroneous top-level _FDTDStaggered*.so glob from %%files — the cython extensions are inside BabelViscoFDTD/ (already covered). * Sun Feb 01 2026 Morgan Hough - 1.0.6-1 - Initial package