%global srcname dolfinx %global upver 0.11.0.post0 %global sover 0.11 # DOLFINx compiles heavily templated C++; an LTO link of those objects is the # peak memory consumer and can spike to several GB in a single process. Disable # LTO so this builds on memory-constrained machines. %global _lto_cflags %{nil} # Cap parallelism by available memory (~4GB per job) instead of one job per # core. %%limit_build emits the -jN flag, so it is substituted here rather than # invoked as a standalone command. %global _smp_mflags %{limit_build -m 4096} # Fedora's MPI packaging conventions: build one flavour per MPI implementation # into %%{_libdir}/. Only openmpi is built here; add mpich by duplicating # the build/install/files blocks with the %%{_mpich_*} macros. Name: fenics-dolfinx Version: 0.11.0 Release: %autorelease Summary: Next-generation FEniCS problem solving environment License: LGPL-3.0-or-later URL: https://github.com/FEniCS/dolfinx Source: %{url}/archive/v%{upver}/%{srcname}-%{upver}.tar.gz BuildRequires: cmake >= 3.21 BuildRequires: gcc-c++ BuildRequires: boost-devel >= 1.70 BuildRequires: pugixml-devel BuildRequires: spdlog-devel BuildRequires: fenics-basix-devel >= 0.11.0 BuildRequires: ufcx-devel >= 0.11.0 BuildRequires: openmpi-devel BuildRequires: hdf5-openmpi-devel BuildRequires: petsc-openmpi-devel >= 3.15 BuildRequires: ptscotch-openmpi-devel # ptscotch-openmpi-devel's SCOTCHConfig.cmake unconditionally include()s # scotchmetisTargets.cmake and ptscotchparmetisTargets.cmake, which are shipped # only by the -parmetis subpackage with no dependency declared between them. # Without this, find_package(SCOTCH) fails outright. (Fedora packaging bug.) BuildRequires: ptscotch-openmpi-devel-parmetis BuildRequires: superlu_dist-openmpi-devel BuildRequires: python3-devel BuildRequires: python3-nanobind >= 2.5.0 BuildRequires: python3-scikit-build-core >= 0.11 # Python bindings are built in %%install against the staged C++ library, so the # pyproject tooling is pulled in explicitly (no %%generate_buildrequires here). BuildRequires: python3-pip BuildRequires: python3-wheel BuildRequires: python3-mpi4py-openmpi BuildRequires: python3-petsc-openmpi BuildRequires: python3-fenics-basix >= 0.11.0 BuildRequires: python3-fenics-ffcx >= 0.11.0 BuildRequires: python3-fenics-ufl >= 2026.1.0 BuildRequires: python3-numpy >= 2 %global _description %{expand: DOLFINx is the computational environment of FEniCSx and implements the FEniCS Problem Solving Environment in C++ and Python. It is the successor to the legacy DOLFIN library, providing mesh handling, function spaces, assembly of finite element forms and interfaces to PETSc linear algebra. Note: SLEPc, ADIOS2 and KaHIP support are disabled because those libraries are not currently packaged in Fedora.} %description %_description %package openmpi Summary: %{summary} (openmpi) Requires: openmpi %description openmpi %_description %package openmpi-devel Summary: Development files for DOLFINx (openmpi) Requires: %{name}-openmpi%{?_isa} = %{version}-%{release} Requires: fenics-basix-devel%{?_isa} Requires: ufcx-devel Requires: cmake-filesystem %description openmpi-devel Headers and CMake configuration for building C++ applications against the openmpi build of DOLFINx. %package -n python3-dolfinx-openmpi Summary: Python interface to DOLFINx (openmpi) Requires: %{name}-openmpi%{?_isa} = %{version}-%{release} Requires: python3-mpi4py-openmpi # petsc4py, needed by dolfinx.fem.petsc Requires: python3-petsc-openmpi Requires: python3-fenics-basix >= 0.11.0 Requires: python3-fenics-ffcx >= 0.11.0 Requires: python3-fenics-ufl >= 2026.1.0 %description -n python3-dolfinx-openmpi Python (nanobind) interface to DOLFINx, built against openmpi. Load the openmpi environment module before use: module load mpi/openmpi-%{_arch} %prep %autosetup -n %{srcname}-%{upver} # GCC 16's omp.h declares C++ templates (the OpenMP allocator helpers), so it can # no longer be included inside an extern "C" block. superlu_dist.cpp wraps the # SuperLU_DIST headers in one, and superlu_ddefs.h -> superlu_defs.h -> omp.h, # so every template in omp.h lands with C linkage and the build stops: # /usr/lib/gcc/.../16/include/omp.h:455: error: template with C linkage # superlu_dist.cpp:10: note: 'extern "C"' linkage started here # (11036717, f45-aarch64). # # gcc is NOT what distinguishes f45 here -- do not file this as an f45 gcc 16 # transition. f44 and f45 both ship gcc 16.2.1-2 with the same templated omp.h, # and 10983216 built this very file on f44-aarch64 with it on 2026-09-14. # The whole difference is one flag: the f45 compile line carries -fopenmp and # the f44 one does not. superlu_defs.h guards its omp.h behind #ifdef _OPENMP # -- identically in 8.2.0 (f44) and 9.2.1 (f45) -- so without -fopenmp omp.h # never enters the extern "C" block at all. The superlu_dist version bump is a # red herring; the .pc Cflags are the same and neither declares Requires:. # # The -fopenmp comes from PETSc, and the chain is: # # 1. Fedora builds petsc 3.25.5-3.fc45 --with-openmp. 3.24.6-4.fc44 is not. # 2. So PETSc.pc differs: # f44 Cflags: -I/usr/include/openmpi-/petsc # f45 Cflags: -fopenmp -I/usr/include/openmpi-/petsc # 3. dolfinx does pkg_search_module(PETSC ... IMPORTED_TARGET) and then # cpp/dolfinx/CMakeLists.txt:138 target_link_libraries(dolfinx PUBLIC # PkgConfig::PETSC) -- PUBLIC, so the Cflags land in # INTERFACE_COMPILE_OPTIONS and every single dolfinx TU gets -fopenmp. # (All 30 logged ninja steps on f45 carry it; zero on f44.) # 4. _OPENMP is now defined, superlu_defs.h pulls omp.h, and it lands inside # the extern "C" -> templates with C linkage. # # CMake never prints "Found OpenMP" because no find_package(OpenMP) happens # here -- which is exactly why grepping the configure output found nothing. # # Two consequences worth knowing: # * Not latent on f44. f44 builds clean today, and will keep doing so until # its petsc is rebuilt --with-openmp. # * NOT specific to this package. Anything on f45 that links PkgConfig::PETSC # now compiles with -fopenmp whether it asked for it or not. Any such # package that wraps a C library's headers in extern "C" will hit this same # omp.h error under gcc 16. # # Pull omp.h in first, with C++ linkage, so its include guard makes the nested # include a no-op. Cheaper and less invasive than unwrapping the extern "C", # which is there because the SuperLU_DIST headers genuinely are C. sed -i '0,/^extern "C"$/s//#include \nextern "C"/' \ cpp/dolfinx/la/superlu_dist.cpp if ! grep -q '^#include $' cpp/dolfinx/la/superlu_dist.cpp; then echo "ERROR: omp.h pre-include not applied to superlu_dist.cpp" >&2 exit 1 fi # The extension module sets INSTALL_RPATH_USE_LINK_PATH as a *target property*, # which overrides the -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=OFF cache variable and # appends the staged buildroot lib dir to its RPATH (check-buildroot then # aborts). CMAKE_INSTALL_RPATH already records the correct final location, so # drop the property. sed -i '/INSTALL_RPATH_USE_LINK_PATH TRUE/d' python/CMakeLists.txt # get_petsc_lib() locates libpetsc via petsc4py's petsc.cfg, but Fedora's # petsc4py records PETSC_DIR as the RPM *build* directory # (/builddir/build/BUILD/petsc-*/...), which exists nowhere at runtime -- so # dolfinx.fem.petsc fails to import on any installed system. Add Fedora's real # library as a candidate. Use the versioned runtime soname so this does not # drag in petsc-openmpi-devel at runtime. Only one candidate can match, or # upstream raises "More than one PETSc shared library found" -- the upstream # paths never exist here, so exactly one does. sed -i 's|^ candidate_paths = \[| candidate_paths = [\n "%{_libdir}/openmpi/lib/libpetsc.so.3",|' \ python/dolfinx/fem/petsc.py %build %{_openmpi_load} export CXXFLAGS="%{optflags}" %cmake -S cpp \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=%{_libdir}/openmpi \ -DCMAKE_INSTALL_LIBDIR=lib \ -DDOLFINX_ENABLE_SLEPC=OFF \ -DDOLFINX_ENABLE_ADIOS2=OFF \ -DDOLFINX_ENABLE_KAHIP=OFF \ -DDOLFINX_ENABLE_PETSC=ON \ -DDOLFINX_ENABLE_SCOTCH=ON \ -DDOLFINX_ENABLE_SUPERLU_DIST=ON %cmake_build %{_openmpi_unload} %install %{_openmpi_load} %cmake_install # Python interface, linked against the staged C++ library export CMAKE_PREFIX_PATH="%{buildroot}%{_libdir}/openmpi:${CMAKE_PREFIX_PATH}" export LD_LIBRARY_PATH="%{buildroot}%{_libdir}/openmpi/lib:${LD_LIBRARY_PATH}" # The MPI prefix is not on the default loader path, so the extension module # does need an RPATH -- but it must record the *final* install location, not # the buildroot it links against here (check-buildroot aborts otherwise). export CMAKE_ARGS="-DDOLFINX_DIR=%{buildroot}%{_libdir}/openmpi/lib/cmake/dolfinx \ -DCMAKE_INSTALL_RPATH=%{_libdir}/openmpi/lib \ -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=OFF" pushd python %pyproject_wheel %pyproject_install popd # Fedora MPI guidelines: MPI-flavoured Python modules must be namespaced per # implementation (as mpi4py/petsc4py are), otherwise the openmpi and mpich # builds would install the same paths and conflict. mkdir -p %{buildroot}%{python3_sitearch}/openmpi mv %{buildroot}%{python3_sitearch}/dolfinx \ %{buildroot}%{python3_sitearch}/openmpi/ mv %{buildroot}%{python3_sitearch}/fenics_dolfinx-*.dist-info \ %{buildroot}%{python3_sitearch}/openmpi/ %{_openmpi_unload} %check %{_openmpi_load} # The extension's RPATH points at the final install location, which is not # populated yet, so point the loader at the staged library for the import test. export LD_LIBRARY_PATH="%{buildroot}%{_libdir}/openmpi/lib:${LD_LIBRARY_PATH}" export PYTHONPATH="%{buildroot}%{python3_sitearch}/openmpi" python3 -c "import dolfinx, dolfinx.fem, dolfinx.mesh, dolfinx.io; print('dolfinx', dolfinx.__version__)" # Exercises the petsc.cfg workaround applied in %%prep: if the candidate path # is wrong these raise RuntimeError, so this is a real regression test rather # than a smoke check. python3 -c "import dolfinx.fem.petsc; print('fem.petsc OK')" python3 -c "import dolfinx.nls.petsc; print('nls.petsc OK')" %{_openmpi_unload} %files openmpi %license COPYING COPYING.LESSER %doc README.md ChangeLog.rst AUTHORS %{_libdir}/openmpi/lib/libdolfinx.so.%{sover}* # Headers stay under the MPI prefix so the exported CMake target's # INTERFACE_INCLUDE_DIRECTORIES resolves to a path that actually exists. %files openmpi-devel %{_libdir}/openmpi/include/dolfinx/ %{_libdir}/openmpi/include/dolfinx.h %{_libdir}/openmpi/lib/libdolfinx.so %{_libdir}/openmpi/lib/cmake/dolfinx/ %{_libdir}/openmpi/lib/pkgconfig/dolfinx.pc %{_libdir}/openmpi/lib/dolfinx/dolfinx.conf %files -n python3-dolfinx-openmpi %license COPYING COPYING.LESSER %{python3_sitearch}/openmpi/dolfinx/ %{python3_sitearch}/openmpi/fenics_dolfinx-%{upver}.dist-info/ %changelog %autochangelog