From 04602c85367b0fc4b094773542d81d9131873f41 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:06:10 +0200 Subject: [PATCH 01/28] Add py-proteus Proteus (proteustoolkit.org): PDE/multiphysics toolkit built around PETSc for parallel linear/nonlinear solves. This first pass deliberately omits Chrono (rigid-body/FSI coupling) and SCOREC/PUMI (parallel mesh adaptation) support: both are optional at the proteus code level, and neither has an upstream Spack package yet (pychrono in particular would be a separate, nontrivial packaging effort). Sources from cekees/proteus's torino_narwhal branch rather than erdc/proteus's tagged releases: the Chrono/SCOREC-optional support this "no chrono" package design depends on (the PROTEUS_SKIP_PUMI_CHRONO env var, and a try/except around an otherwise-unconditional Chrono config-file lookup) hasn't been released upstream yet. Revisit tracking erdc/proteus tags once that support lands there. Depends on two other fixes proposed alongside this PR (not yet merged): xtensor-python@0.28.0 (this branch/PR) and xtensor's xsimd upper bound for 0.26.0:0.27 (this branch/PR) -- xtensor-python was previously stuck at 0.23.1, and xtensor's own xsimd constraint was too permissive for the 0.26/0.27 line. Built and verified working end-to-end (spack install + import proteus + petsc4py Comm.init()) against a from-scratch toolchain, including building openmpi/petsc/etc. from source rather than relying on any external MPI. --- .../builtin/packages/py_proteus/package.py | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 repos/spack_repo/builtin/packages/py_proteus/package.py diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py new file mode 100644 index 00000000000..4cba175a770 --- /dev/null +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -0,0 +1,119 @@ +# Copyright Spack Project Developers. See COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.python import PythonPackage + +from spack.package import * + + +class PyProteus(PythonPackage): + """Proteus: Computational Methods and Simulation Toolkit. Python tools + for rapidly developing computer models and numerical methods, built + around PETSc for parallel linear/nonlinear solves.""" + + homepage = "http://proteustoolkit.org" + # NOTE: exploratory choice (not the conda-forge convention, which tracks + # erdc/proteus release tags) -- erdc/proteus@1.8.3's config/default.py + # unconditionally requires a Chrono install (unguarded open() of + # chrono-config.cmake), matching conda-forge's proteus-feedstock, which + # also hard-depends on pychrono. The PROTEUS_SKIP_PUMI_CHRONO env var + # and the try/except around that open() call (making Chrono/SCOREC + # genuinely optional, which is the whole point of this first pass) only + # exist on cekees/proteus's torino_narwhal branch, not yet released + # upstream. Revisit tracking erdc/proteus tags once that support lands + # there. + git = "https://github.com/cekees/proteus.git" + + maintainers("cekees") + + license("MIT") + + version("torino_narwhal", branch="torino_narwhal") + + # This first pass deliberately omits Chrono (rigid-body/FSI coupling) + # and SCOREC/PUMI (parallel mesh adaptation) support: both are optional + # at the proteus code level (proteus/Domain.py degrades gracefully + # without MeshAdaptPUMI.MeshAdapt; setup.py can skip both extensions + # via PROTEUS_SKIP_PUMI_CHRONO, used unconditionally below), and neither + # has an upstream Spack package yet -- pychrono in particular would be + # a separate, nontrivial packaging effort. Revisit as `scorec`/`chrono` + # variants once those exist upstream. + + depends_on("c", type="build") + depends_on("cxx", type="build") + depends_on("fortran", type="build") + + depends_on("python@3.9:", type=("build", "run")) + depends_on("py-setuptools@61:", type="build") + depends_on("py-cython@3:", type="build") + # pybind11 3.x is excluded: combined with xtensor@0.27.1 it triggers a + # genuine ambiguous-overload compile error in xtensor's own xsemantic.hpp + # (operator*= on xt::pyarray, hit from proteus/mprans/SW2DCV.h) that + # doesn't reproduce with the pybind11 2.13.x + xtensor 0.26.0 combination + # already validated (this session, outside Spack) against the same + # proteus source. + depends_on("py-pybind11@2.11:2", type="build") + + depends_on("py-numpy@1.25:", type=("build", "run")) + depends_on("py-scipy", type=("build", "run")) + depends_on("py-mpi4py", type=("build", "run")) + depends_on("py-petsc4py", type=("build", "run")) + depends_on("py-h5py+mpi", type=("build", "run")) + + depends_on("mpi") + depends_on("petsc+mpi") + depends_on("hdf5+mpi+hl") + # proteus/config/default.py hard-codes '-lopenblas' on Linux -- not a + # generic blas/lapack virtual dependency. + depends_on("openblas") + depends_on("superlu") + depends_on("triangle") + # Only proteus.fenton.Fenton links ncurses; unlike Chrono/SCOREC it has + # no build-time skip switch, so it's a hard dependency here. + depends_on("ncurses") + + # Header-only C++ dependencies. setup.py's get_xtensor_include() (used + # by ~30 of proteus's extensions) looks under sys.prefix/include, not + # any per-package *_DIR env var, so these need to land on the compiler's + # search path explicitly -- see setup_build_environment below. + # Constrained to the long-standing 3.4 branch since Eigen jumped + # straight from there to a new 5.0.x major version -- kept as a + # conservative pin even though it turned out NOT to be the cause of + # the operator*= ambiguity below (that's purely an xtensor/pybind11 + # interaction, verified by testing eigen@3.4 alone first). + depends_on("eigen@3.4") + # Pinned to 0.26.0 (not just "xtensor-python@0.28.0:'s @0.26.0:" lower + # bound) -- xtensor@0.27.1 combined with py-pybind11@3.x produces a + # genuine ambiguous 'operator*=' compile error in xtensor's own + # xsemantic.hpp when proteus/mprans/SW2DCV.h calls it on an + # xt::pyarray; 0.26.0 is the exact version already validated (this + # session, outside Spack) against this proteus source. + depends_on("xtensor@0.26.0") + # xtensor-python@0.28.0 is an exploratory addition to the local + # xtensor_python package (not yet upstreamed) -- Spack's builtin only + # had 0.23.1, which forces an ancient py-pybind11@2.2 incompatible + # with any modern py-scipy. + depends_on("xtensor-python@0.28.0:") + depends_on("xtl") + + def setup_build_environment(self, env): + env.set("PROTEUS_SKIP_PUMI_CHRONO", "1") + + # proteus/config/default.py's get_flags() resolves each dependency + # via _DIR (falling back to one shared PROTEUS_PREFIX) -- + # Spack gives every dependency its own prefix, so set them all + # individually rather than relying on the single-prefix fallback. + env.set("PETSC_DIR", self.spec["petsc"].prefix) + env.set("MPI_DIR", self.spec["mpi"].prefix) + env.set("HDF5_DIR", self.spec["hdf5"].prefix) + env.set("BLAS_DIR", self.spec["openblas"].prefix) + env.set("LAPACK_DIR", self.spec["openblas"].prefix) + env.set("SUPERLU_DIR", self.spec["superlu"].prefix) + env.set("TRIANGLE_DIR", self.spec["triangle"].prefix) + env.set("NCURSES_DIR", self.spec["ncurses"].prefix) + + # Headers-only deps setup.py can't be told about via a *_DIR env + # var -- fall back to the standard compiler search-path variables. + for dep in ("eigen", "xtensor", "xtensor-python", "xtl"): + env.prepend_path("CPATH", self.spec[dep].prefix.include) From c5a7d4143bc06a2a2ff3f7da6e61843d677958cc Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:16:24 +0200 Subject: [PATCH 02/28] py-proteus: add scorec variant wiring PUMI in instead of skipping it setup.py gated MeshAdaptPUMI.MeshAdapt and mbd.CouplingFSI behind one PROTEUS_SKIP_PUMI_CHRONO switch. Chrono stays skipped unconditionally (pychrono has no upstream Spack package), but PUMI does have one now (`pumi`), so the new `scorec` variant (default off) depends on pumi+zoltan+shared, zoltan+parmetis~fortran, and parmetis, points SCOREC_DIR/ZOLTAN_DIR/PARMETIS_DIR at them, and applies split-pumi-chrono-skip.patch to split the single skip switch into independent PROTEUS_SKIP_PUMI/PROTEUS_SKIP_CHRONO ones so PUMI can be enabled while Chrono stays off. pumi needs +shared explicitly: it defaults to static libs without -fPIC, which can't link into MeshAdaptPUMI.MeshAdapt (a shared Python extension). zoltan needs ~fortran: its Fortran interface is unused here and its shared lib leaves unresolved libgfortran symbols on this toolchain when built with +fortran. Verified end to end: py-proteus+scorec builds and proteus.MeshAdaptPUMI.MeshAdapt imports and resolves its shared libraries correctly at runtime. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01MraeTXJFpMRZJUdYthLxyg --- .../builtin/packages/py_proteus/package.py | 52 +++++++++++++++---- .../py_proteus/split-pumi-chrono-skip.patch | 20 +++++++ 2 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 4cba175a770..cd0f100f483 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -31,14 +31,22 @@ class PyProteus(PythonPackage): version("torino_narwhal", branch="torino_narwhal") - # This first pass deliberately omits Chrono (rigid-body/FSI coupling) - # and SCOREC/PUMI (parallel mesh adaptation) support: both are optional - # at the proteus code level (proteus/Domain.py degrades gracefully - # without MeshAdaptPUMI.MeshAdapt; setup.py can skip both extensions - # via PROTEUS_SKIP_PUMI_CHRONO, used unconditionally below), and neither - # has an upstream Spack package yet -- pychrono in particular would be - # a separate, nontrivial packaging effort. Revisit as `scorec`/`chrono` - # variants once those exist upstream. + # Chrono (rigid-body/FSI coupling) is still omitted unconditionally: + # it's optional at the proteus code level (setup.py's mbd.CouplingFSI + # extension is always skippable), but pychrono has no upstream Spack + # package yet and would be a separate, nontrivial packaging effort. + # + # SCOREC/PUMI (parallel mesh adaptation), however, *does* have an + # upstream Spack package (`pumi`) -- the `scorec` variant below wires + # setup.py's MeshAdaptPUMI.MeshAdapt extension up to it instead of + # skipping it. setup.py originally gated MeshAdaptPUMI and + # mbd.CouplingFSI behind the single PROTEUS_SKIP_PUMI_CHRONO switch; + # split-pumi-chrono-skip.patch splits that into independent + # PROTEUS_SKIP_PUMI / PROTEUS_SKIP_CHRONO switches so PUMI can be + # enabled while Chrono stays skipped. + variant("scorec", default=False, description="Enable SCOREC/PUMI mesh adaptation support") + + patch("split-pumi-chrono-skip.patch") depends_on("c", type="build") depends_on("cxx", type="build") @@ -73,6 +81,23 @@ class PyProteus(PythonPackage): # no build-time skip switch, so it's a hard dependency here. depends_on("ncurses") + # config/default.py's PROTEUS_SCOREC_LIBS links 'zoltan', 'parmetis', + # and 'metis' directly (not just transitively through pumi), so all + # three need their own *_DIR env vars set in setup_build_environment + # below; zoltan+parmetis is required so pumi's apf_zoltan is actually + # built against ParMETIS (matching what config/default.py expects to + # link against). + # +shared: pumi defaults to static libs (~shared), which aren't built + # with -fPIC and can't be linked into MeshAdaptPUMI.MeshAdapt (a + # shared Python extension) -- setup.py has no static-linking path. + depends_on("pumi+zoltan+shared", when="+scorec") + # ~fortran: zoltan's Fortran interface is unused here (PUMI's + # apf_zoltan and proteus's own linking only ever call Zoltan's C API), + # and zoltan@3.901's libzoltan.so leaves unresolved libgfortran + # symbols when +fortran is built on this toolchain. + depends_on("zoltan+parmetis~fortran", when="+scorec") + depends_on("parmetis", when="+scorec") + # Header-only C++ dependencies. setup.py's get_xtensor_include() (used # by ~30 of proteus's extensions) looks under sys.prefix/include, not # any per-package *_DIR env var, so these need to land on the compiler's @@ -98,7 +123,11 @@ class PyProteus(PythonPackage): depends_on("xtl") def setup_build_environment(self, env): - env.set("PROTEUS_SKIP_PUMI_CHRONO", "1") + # Chrono/pychrono is never packaged here (see the `scorec` variant + # comment above), so mbd.CouplingFSI is always skipped. + env.set("PROTEUS_SKIP_CHRONO", "1") + if self.spec.satisfies("~scorec"): + env.set("PROTEUS_SKIP_PUMI", "1") # proteus/config/default.py's get_flags() resolves each dependency # via _DIR (falling back to one shared PROTEUS_PREFIX) -- @@ -113,6 +142,11 @@ def setup_build_environment(self, env): env.set("TRIANGLE_DIR", self.spec["triangle"].prefix) env.set("NCURSES_DIR", self.spec["ncurses"].prefix) + if self.spec.satisfies("+scorec"): + env.set("SCOREC_DIR", self.spec["pumi"].prefix) + env.set("ZOLTAN_DIR", self.spec["zoltan"].prefix) + env.set("PARMETIS_DIR", self.spec["parmetis"].prefix) + # Headers-only deps setup.py can't be told about via a *_DIR env # var -- fall back to the standard compiler search-path variables. for dep in ("eigen", "xtensor", "xtensor-python", "xtl"): diff --git a/repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch b/repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch new file mode 100644 index 00000000000..e76670784f1 --- /dev/null +++ b/repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch @@ -0,0 +1,20 @@ +diff --git a/setup.py b/setup.py +index 299f1d1f..59d6485e 100644 +--- a/setup.py ++++ b/setup.py +@@ -802,9 +802,12 @@ EXTENSIONS_TO_BUILD = [ + ] + + import os as _os +-if _os.environ.get("PROTEUS_SKIP_PUMI_CHRONO"): +- _skip = {"MeshAdaptPUMI.MeshAdapt", "mbd.CouplingFSI"} +- EXTENSIONS_TO_BUILD = [e for e in EXTENSIONS_TO_BUILD if e.name not in _skip] ++_skip = set() ++if _os.environ.get("PROTEUS_SKIP_PUMI_CHRONO") or _os.environ.get("PROTEUS_SKIP_PUMI"): ++ _skip.add("MeshAdaptPUMI.MeshAdapt") ++if _os.environ.get("PROTEUS_SKIP_PUMI_CHRONO") or _os.environ.get("PROTEUS_SKIP_CHRONO"): ++ _skip.add("mbd.CouplingFSI") ++EXTENSIONS_TO_BUILD = [e for e in EXTENSIONS_TO_BUILD if e.name not in _skip] + + def setup_given_extensions(extensions): + setup(name='proteus', From 8b26624627cb4ab45b2f34b0c6dd3b3cdec0aea4 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:40:09 +0200 Subject: [PATCH 03/28] py-proteus: drop split-pumi-chrono-skip.patch, now upstream on torino_narwhal cekees/proteus@torino_narwhal now natively splits PROTEUS_SKIP_PUMI_CHRONO into independent PROTEUS_SKIP_PUMI/PROTEUS_SKIP_CHRONO switches (pushed directly to that branch), so the Spack-side patch is redundant -- and would otherwise break the next fetch, since its "before" context (the single combined switch) no longer exists in the source. Verified py-proteus+scorec builds against the new commit with no patch and MeshAdaptPUMI.MeshAdapt still imports correctly. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01MraeTXJFpMRZJUdYthLxyg --- .../builtin/packages/py_proteus/package.py | 21 +++++++------------ .../py_proteus/split-pumi-chrono-skip.patch | 20 ------------------ 2 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index cd0f100f483..d4c47578bd8 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -17,12 +17,12 @@ class PyProteus(PythonPackage): # erdc/proteus release tags) -- erdc/proteus@1.8.3's config/default.py # unconditionally requires a Chrono install (unguarded open() of # chrono-config.cmake), matching conda-forge's proteus-feedstock, which - # also hard-depends on pychrono. The PROTEUS_SKIP_PUMI_CHRONO env var - # and the try/except around that open() call (making Chrono/SCOREC - # genuinely optional, which is the whole point of this first pass) only - # exist on cekees/proteus's torino_narwhal branch, not yet released - # upstream. Revisit tracking erdc/proteus tags once that support lands - # there. + # also hard-depends on pychrono. The try/except around that open() call, + # and the PROTEUS_SKIP_PUMI/PROTEUS_SKIP_CHRONO env vars (making + # Chrono/SCOREC independently optional, which is the whole point of + # this first pass) only exist on cekees/proteus's torino_narwhal + # branch, not yet released upstream. Revisit tracking erdc/proteus + # tags once that support lands there. git = "https://github.com/cekees/proteus.git" maintainers("cekees") @@ -39,15 +39,10 @@ class PyProteus(PythonPackage): # SCOREC/PUMI (parallel mesh adaptation), however, *does* have an # upstream Spack package (`pumi`) -- the `scorec` variant below wires # setup.py's MeshAdaptPUMI.MeshAdapt extension up to it instead of - # skipping it. setup.py originally gated MeshAdaptPUMI and - # mbd.CouplingFSI behind the single PROTEUS_SKIP_PUMI_CHRONO switch; - # split-pumi-chrono-skip.patch splits that into independent - # PROTEUS_SKIP_PUMI / PROTEUS_SKIP_CHRONO switches so PUMI can be - # enabled while Chrono stays skipped. + # skipping it, via the independent PROTEUS_SKIP_PUMI switch (see + # setup_build_environment below). variant("scorec", default=False, description="Enable SCOREC/PUMI mesh adaptation support") - patch("split-pumi-chrono-skip.patch") - depends_on("c", type="build") depends_on("cxx", type="build") depends_on("fortran", type="build") diff --git a/repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch b/repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch deleted file mode 100644 index e76670784f1..00000000000 --- a/repos/spack_repo/builtin/packages/py_proteus/split-pumi-chrono-skip.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/setup.py b/setup.py -index 299f1d1f..59d6485e 100644 ---- a/setup.py -+++ b/setup.py -@@ -802,9 +802,12 @@ EXTENSIONS_TO_BUILD = [ - ] - - import os as _os --if _os.environ.get("PROTEUS_SKIP_PUMI_CHRONO"): -- _skip = {"MeshAdaptPUMI.MeshAdapt", "mbd.CouplingFSI"} -- EXTENSIONS_TO_BUILD = [e for e in EXTENSIONS_TO_BUILD if e.name not in _skip] -+_skip = set() -+if _os.environ.get("PROTEUS_SKIP_PUMI_CHRONO") or _os.environ.get("PROTEUS_SKIP_PUMI"): -+ _skip.add("MeshAdaptPUMI.MeshAdapt") -+if _os.environ.get("PROTEUS_SKIP_PUMI_CHRONO") or _os.environ.get("PROTEUS_SKIP_CHRONO"): -+ _skip.add("mbd.CouplingFSI") -+EXTENSIONS_TO_BUILD = [e for e in EXTENSIONS_TO_BUILD if e.name not in _skip] - - def setup_given_extensions(extensions): - setup(name='proteus', From d257f3d482a4cfbd4e46e471680edbf78441b0a2 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:34:18 +0200 Subject: [PATCH 04/28] py-proteus: trim comments to essentials Comments were written narrating the investigation (what was tried, what was validated when, "this session"); condense each down to the non-obvious fact a maintainer actually needs -- the constraint or workaround itself, not the story behind finding it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01MraeTXJFpMRZJUdYthLxyg --- .../builtin/packages/py_proteus/package.py | 92 +++++-------------- 1 file changed, 22 insertions(+), 70 deletions(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index d4c47578bd8..10a09cda03f 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -13,16 +13,9 @@ class PyProteus(PythonPackage): around PETSc for parallel linear/nonlinear solves.""" homepage = "http://proteustoolkit.org" - # NOTE: exploratory choice (not the conda-forge convention, which tracks - # erdc/proteus release tags) -- erdc/proteus@1.8.3's config/default.py - # unconditionally requires a Chrono install (unguarded open() of - # chrono-config.cmake), matching conda-forge's proteus-feedstock, which - # also hard-depends on pychrono. The try/except around that open() call, - # and the PROTEUS_SKIP_PUMI/PROTEUS_SKIP_CHRONO env vars (making - # Chrono/SCOREC independently optional, which is the whole point of - # this first pass) only exist on cekees/proteus's torino_narwhal - # branch, not yet released upstream. Revisit tracking erdc/proteus - # tags once that support lands there. + # Tracks cekees/proteus's torino_narwhal branch rather than erdc/proteus + # releases: upstream unconditionally requires a Chrono install. + # torino_narwhal makes Chrono and SCOREC independently optional. git = "https://github.com/cekees/proteus.git" maintainers("cekees") @@ -31,16 +24,9 @@ class PyProteus(PythonPackage): version("torino_narwhal", branch="torino_narwhal") - # Chrono (rigid-body/FSI coupling) is still omitted unconditionally: - # it's optional at the proteus code level (setup.py's mbd.CouplingFSI - # extension is always skippable), but pychrono has no upstream Spack - # package yet and would be a separate, nontrivial packaging effort. - # - # SCOREC/PUMI (parallel mesh adaptation), however, *does* have an - # upstream Spack package (`pumi`) -- the `scorec` variant below wires - # setup.py's MeshAdaptPUMI.MeshAdapt extension up to it instead of - # skipping it, via the independent PROTEUS_SKIP_PUMI switch (see - # setup_build_environment below). + # Chrono (pychrono) has no upstream Spack package and stays disabled. + # SCOREC/PUMI does (`pumi`); this variant wires it in instead of + # always skipping it. variant("scorec", default=False, description="Enable SCOREC/PUMI mesh adaptation support") depends_on("c", type="build") @@ -50,12 +36,8 @@ class PyProteus(PythonPackage): depends_on("python@3.9:", type=("build", "run")) depends_on("py-setuptools@61:", type="build") depends_on("py-cython@3:", type="build") - # pybind11 3.x is excluded: combined with xtensor@0.27.1 it triggers a - # genuine ambiguous-overload compile error in xtensor's own xsemantic.hpp - # (operator*= on xt::pyarray, hit from proteus/mprans/SW2DCV.h) that - # doesn't reproduce with the pybind11 2.13.x + xtensor 0.26.0 combination - # already validated (this session, outside Spack) against the same - # proteus source. + # pybind11 3.x + xtensor@0.27.1 fails to compile: ambiguous operator*= + # overload in xtensor's xsemantic.hpp. depends_on("py-pybind11@2.11:2", type="build") depends_on("py-numpy@1.25:", type=("build", "run")) @@ -67,67 +49,39 @@ class PyProteus(PythonPackage): depends_on("mpi") depends_on("petsc+mpi") depends_on("hdf5+mpi+hl") - # proteus/config/default.py hard-codes '-lopenblas' on Linux -- not a - # generic blas/lapack virtual dependency. + # config/default.py hard-codes '-lopenblas' on Linux. depends_on("openblas") depends_on("superlu") depends_on("triangle") - # Only proteus.fenton.Fenton links ncurses; unlike Chrono/SCOREC it has - # no build-time skip switch, so it's a hard dependency here. + # proteus.fenton.Fenton links ncurses unconditionally. depends_on("ncurses") - # config/default.py's PROTEUS_SCOREC_LIBS links 'zoltan', 'parmetis', - # and 'metis' directly (not just transitively through pumi), so all - # three need their own *_DIR env vars set in setup_build_environment - # below; zoltan+parmetis is required so pumi's apf_zoltan is actually - # built against ParMETIS (matching what config/default.py expects to - # link against). - # +shared: pumi defaults to static libs (~shared), which aren't built - # with -fPIC and can't be linked into MeshAdaptPUMI.MeshAdapt (a - # shared Python extension) -- setup.py has no static-linking path. + # zoltan and parmetis are linked directly, not just via pumi, so each + # needs its own *_DIR in setup_build_environment. + # +shared: pumi's static libs aren't built with -fPIC and can't link + # into the shared MeshAdaptPUMI.MeshAdapt extension. depends_on("pumi+zoltan+shared", when="+scorec") - # ~fortran: zoltan's Fortran interface is unused here (PUMI's - # apf_zoltan and proteus's own linking only ever call Zoltan's C API), - # and zoltan@3.901's libzoltan.so leaves unresolved libgfortran - # symbols when +fortran is built on this toolchain. + # ~fortran: unused here, and zoltan's Fortran interface leaves + # unresolved libgfortran symbols in libzoltan.so on this toolchain. depends_on("zoltan+parmetis~fortran", when="+scorec") depends_on("parmetis", when="+scorec") - # Header-only C++ dependencies. setup.py's get_xtensor_include() (used - # by ~30 of proteus's extensions) looks under sys.prefix/include, not - # any per-package *_DIR env var, so these need to land on the compiler's - # search path explicitly -- see setup_build_environment below. - # Constrained to the long-standing 3.4 branch since Eigen jumped - # straight from there to a new 5.0.x major version -- kept as a - # conservative pin even though it turned out NOT to be the cause of - # the operator*= ambiguity below (that's purely an xtensor/pybind11 - # interaction, verified by testing eigen@3.4 alone first). + # Header-only deps: get_xtensor_include() looks under sys.prefix/include + # rather than a *_DIR var, so these are added to CPATH instead. depends_on("eigen@3.4") - # Pinned to 0.26.0 (not just "xtensor-python@0.28.0:'s @0.26.0:" lower - # bound) -- xtensor@0.27.1 combined with py-pybind11@3.x produces a - # genuine ambiguous 'operator*=' compile error in xtensor's own - # xsemantic.hpp when proteus/mprans/SW2DCV.h calls it on an - # xt::pyarray; 0.26.0 is the exact version already validated (this - # session, outside Spack) against this proteus source. + # 0.27.1 + py-pybind11@3.x fails to compile (see pybind11 pin above). depends_on("xtensor@0.26.0") - # xtensor-python@0.28.0 is an exploratory addition to the local - # xtensor_python package (not yet upstreamed) -- Spack's builtin only - # had 0.23.1, which forces an ancient py-pybind11@2.2 incompatible - # with any modern py-scipy. + # 0.28.0 is exploratory, added locally to xtensor_python; builtin + # spack-packages only had 0.23.1, which forces pybind11@2.2. depends_on("xtensor-python@0.28.0:") depends_on("xtl") def setup_build_environment(self, env): - # Chrono/pychrono is never packaged here (see the `scorec` variant - # comment above), so mbd.CouplingFSI is always skipped. env.set("PROTEUS_SKIP_CHRONO", "1") if self.spec.satisfies("~scorec"): env.set("PROTEUS_SKIP_PUMI", "1") - # proteus/config/default.py's get_flags() resolves each dependency - # via _DIR (falling back to one shared PROTEUS_PREFIX) -- - # Spack gives every dependency its own prefix, so set them all - # individually rather than relying on the single-prefix fallback. + # config/default.py's get_flags() reads a _DIR per dependency. env.set("PETSC_DIR", self.spec["petsc"].prefix) env.set("MPI_DIR", self.spec["mpi"].prefix) env.set("HDF5_DIR", self.spec["hdf5"].prefix) @@ -142,7 +96,5 @@ def setup_build_environment(self, env): env.set("ZOLTAN_DIR", self.spec["zoltan"].prefix) env.set("PARMETIS_DIR", self.spec["parmetis"].prefix) - # Headers-only deps setup.py can't be told about via a *_DIR env - # var -- fall back to the standard compiler search-path variables. for dep in ("eigen", "xtensor", "xtensor-python", "xtl"): env.prepend_path("CPATH", self.spec[dep].prefix.include) From 44ba7f2809429ea4ec6a082c626e318971e9beb9 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:25:35 +0200 Subject: [PATCH 05/28] triangle: fix macOS build (-DLINUX hardcoded) and install a header+library triangle 1.6's own upstream makefile hardcodes CSWITCHES with -DLINUX unconditionally, regardless of the actual build platform. triangle.c only consults that macro (alongside the mutually-exclusive -DCPU86) to twiddle the legacy x87 FPU's precision-control register on old x86 hardware -- irrelevant on Apple Silicon and unnecessary on modern x86_64 -- but the macro also gates an unconditional #include , a glibc/Linux- only header that doesn't exist on macOS. Building with the stock makefile there failed outright with "fatal error: 'fpu_control.h' file not found". Override CSWITCHES on Darwin to drop -DLINUX; Linux builds are unaffected. Also: this package previously only installed the CLI executables (triangle, showme), nothing a dependent could compile/link against. Consumers like proteus link directly against triangle's C API (a single object file built from triangle.c with -DTRILIBRARY, per the upstream makefile's own "trilibrary" target) rather than shelling out to the CLI. Without an installed header+library, proteus's own build failed with "fatal error: 'triangle.h' file not found". Build and install both, matching what proteus's config/default.py expects: $TRIANGLE_DIR/include/ triangle.h and $TRIANGLE_DIR/lib/libtri.a. Confirmed via a real `spack install py-proteus` build. --- .../builtin/packages/triangle/package.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/triangle/package.py b/repos/spack_repo/builtin/packages/triangle/package.py index d4a9bb43f06..32a76eda457 100644 --- a/repos/spack_repo/builtin/packages/triangle/package.py +++ b/repos/spack_repo/builtin/packages/triangle/package.py @@ -27,8 +27,44 @@ class Triangle(Package): depends_on("gmake", type="build") def install(self, spec, prefix): - make() + # triangle 1.6's own makefile hardcodes CSWITCHES with -DLINUX + # unconditionally, regardless of the actual build platform. triangle.c + # only consults that macro (alongside the mutually-exclusive -DCPU86) + # to twiddle the legacy x87 FPU's precision-control register on old + # x86 hardware -- irrelevant on Apple Silicon (no x87 unit) and + # unnecessary on modern x86_64 (doubles use SSE by default), but the + # macro also gates an unconditional `#include `, a + # glibc/Linux-only header that doesn't exist on macOS at all. Building + # with the stock makefile there fails outright with "fatal error: + # 'fpu_control.h' file not found". Override CSWITCHES on Darwin to + # drop -DLINUX (and skip the header/precision tweak entirely, since + # neither alternative macro applies here); Linux builds are + # unaffected and keep the upstream makefile's own default. + cswitches = "-O -I/usr/X11R6/include -L/usr/X11R6/lib" + if spec.satisfies("platform=darwin"): + make("CSWITCHES=" + cswitches) + else: + make() mkdirp(prefix.bin) install("triangle", prefix.bin) install("showme", prefix.bin) + + # This package previously only installed the CLI executables above, + # nothing a dependent could compile/link against -- but consumers + # like proteus link directly against triangle's C API (a single + # object file, triangle.o, built from triangle.c with -DTRILIBRARY + # per the upstream makefile's own "trilibrary" target) rather than + # shelling out to the CLI. Without an installed header+library, + # proteus's own build fails with "fatal error: 'triangle.h' file not + # found" (confirmed via a real `spack install py-proteus` build). + # Build and install both, matching what proteus's config/default.py + # expects: $TRIANGLE_DIR/include/triangle.h and + # $TRIANGLE_DIR/lib/libtri.a. + make("CSWITCHES=" + cswitches, "trilibrary") + mkdirp(prefix.include) + mkdirp(prefix.lib) + install("triangle.h", prefix.include) + ar = which("ar", required=True) + ar("rcs", "libtri.a", "triangle.o") + install("libtri.a", prefix.lib) From 929d7d48e2f6d387a62070b3cccf48c1102f6609 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:25:35 +0200 Subject: [PATCH 06/28] zoltan: fix gfortran linking and self-declared dylib ID on macOS Two related but distinct macOS linking bugs, both only visible once a consumer actually tries to load the built library: 1. The %gcc branch appends a bare -lgfortran with no matching -L. This silently works when gfortran is a system/Homebrew package (its runtime lib sits on a default linker search path) but fails against Spack's own from-source-built GCC, whose runtime libs live in their own package prefix: "ld: library 'gfortran' not found". Fixed by asking the Fortran compiler itself where its own libgfortran lives (`gfortran -print-file-name=libgfortran.dylib`) rather than guessing a layout, so this works regardless of the actual gcc package Spack picked. 2. This package fakes a shared library via a compiler-as-archiver trick (`--with-ar="$(CXX) -shared $(LDFLAGS) -o"`, since Zoltan's own build system only ever knows how to archive into a .a) and then renames the resulting file from .a to .dylib in solib_install(). The rename is file-system only -- it doesn't touch the dylib's own embedded LC_ID_DYLIB, which clang baked in as the literal, path-less string "libzoltan.a" (the link command's own -o argument) since no explicit -install_name was ever passed. Any consumer linked against this library afterward (pumi's libapf_zoltan.dylib, confirmed via a real `spack install py-proteus+scorec` build) captures that bogus self- reference as *its own* dependency, and fails to dlopen ("Library not loaded: libzoltan.a") however correct its own rpath otherwise is. Fixed by correcting the dylib's self-declared ID via `install_name_tool -id` right after the rename, to match what it now actually is. --- .../builtin/packages/zoltan/package.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/repos/spack_repo/builtin/packages/zoltan/package.py b/repos/spack_repo/builtin/packages/zoltan/package.py index 05fd4eeda55..c7e5cec960f 100644 --- a/repos/spack_repo/builtin/packages/zoltan/package.py +++ b/repos/spack_repo/builtin/packages/zoltan/package.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import os import re from spack_repo.builtin.build_systems.autotools import AutotoolsPackage @@ -119,6 +120,21 @@ def configure_args(self): # Although adding to config_libs _should_ suffice, it does not # Add to ldflags as well config_ldflags.append("-lgfortran") + # A bare -lgfortran only resolves if libgfortran sits on the + # linker's default search path -- true for a system-package- + # manager-provided gcc, but not for a from-source Spack-built + # gcc, whose runtime libs live in its own package prefix. + # Without an explicit -L here this fails outright on macOS + # ("ld: library 'gfortran' not found"; confirmed via a real + # `spack install py-proteus+scorec` build). Ask the Fortran + # compiler itself where its own libgfortran lives rather than + # guessing a layout, so this works regardless of the actual + # gcc package/version Spack picked. + gfortran_lib = Executable(self.compiler.fc)( + "-print-file-name=libgfortran." + dso_suffix, output=str + ).strip() + if gfortran_lib and gfortran_lib != "libgfortran." + dso_suffix: + config_ldflags.append("-L" + os.path.dirname(gfortran_lib)) if spec.satisfies("%intel") or spec.satisfies("%oneapi"): config_libs.append("-lifcore") @@ -211,3 +227,26 @@ def solib_install(self): for lib_path in find(self.spec.prefix.lib, "lib*.a"): lib_shared_name = re.sub(r"\.a$", f".{dso_suffix}", lib_path) move(lib_path, lib_shared_name) + # The `--with-ar="$(CXX) -shared $(LDFLAGS) -o"` trick above + # (needed since Zoltan's own build system only ever knows how to + # *archive* into a .a, never link a real shared library) compiles + # a genuine Mach-O dylib, but names the link command's `-o` + # output "libzoltan.a" -- and without an explicit -install_name, + # clang bakes that literal, path-less string into the dylib's + # own LC_ID_DYLIB. Renaming the file on disk just above doesn't + # touch that embedded metadata: any consumer linked against this + # library afterward (pumi's libapf_zoltan.dylib, confirmed via a + # real `spack install py-proteus+scorec` build) captures + # "libzoltan.a" as *its own* reference -- a bare filename with no + # directory, which no rpath entry can resolve, so it fails to + # dlopen ("Library not loaded: libzoltan.a") however correct its + # own rpath list otherwise is. Fix the dylib's self-declared ID + # to a proper @rpath-relative one to match what it was renamed + # to, so downstream linkers record something resolvable instead. + if dso_suffix == "dylib": + install_name_tool = which("install_name_tool", required=True) + install_name_tool( + "-id", + "@rpath/" + os.path.basename(lib_shared_name), + lib_shared_name, + ) From 9163e5cb2f854a22a955275eb668c380117536be Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:25:35 +0200 Subject: [PATCH 07/28] py-proteus: link metis (needed by SuperLU's ordering), revert local-test git override SuperLU's fill-reducing ordering calls METIS_NodeND, but metis was previously only pulled in transitively via parmetis, and only when +scorec. Spack only wires up the rpath/link-path for a package's *direct* depends_on()s, so the base (~scorec) build's superluWrappers extension failed at import time with "symbol not found in flat namespace '_METIS_NodeND'" (confirmed via a real `spack install py-proteus` build). The actual fix for the missing link flags lives in proteus's own setup.py (config/default.py + setup.py, torino_narwhal branch) -- this package now just needs to depend on metis directly and export METIS_DIR unconditionally so proteus's build can find it, matching the existing per-dependency *_DIR pattern already used here for superlu/triangle/etc. --- .../builtin/packages/py_proteus/package.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 10a09cda03f..518dd4fa387 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -16,6 +16,16 @@ class PyProteus(PythonPackage): # Tracks cekees/proteus's torino_narwhal branch rather than erdc/proteus # releases: upstream unconditionally requires a Chrono install. # torino_narwhal makes Chrono and SCOREC independently optional. + # + # NOTE: github.com/cekees/proteus's fork-network LFS bandwidth budget is + # billed against upstream erdc/proteus, not cekees, and can be exhausted + # independent of anything the cekees fork owner does -- a fresh clone's + # `git checkout` then fails during LFS smudge ("git checkout --quiet + # ...: exit status 128"). If you hit that, point this at a local, + # already-cloned checkout instead (`git = "file:///path/to/checkout"`) + # as a temporary local edit -- Spack has no per-invocation override for + # a version's own git URL (confirmed: `git=` is not a valid spec + # variant here). git = "https://github.com/cekees/proteus.git" maintainers("cekees") @@ -51,6 +61,15 @@ class PyProteus(PythonPackage): depends_on("hdf5+mpi+hl") # config/default.py hard-codes '-lopenblas' on Linux. depends_on("openblas") + # superlu's own library calls METIS_NodeND for fill-reducing ordering, but + # metis was previously only pulled in transitively via parmetis, and only + # when +scorec. Without a direct dependency Spack never adds metis's lib + # dir to the rpath/link-path Spack automatically wires up for direct + # depends_on() packages, so the base (~scorec) build's superluWrappers + # extension fails at import time with "symbol not found in flat + # namespace '_METIS_NodeND'" (confirmed via a real `spack install + # py-proteus` build). + depends_on("metis") depends_on("superlu") depends_on("triangle") # proteus.fenton.Fenton links ncurses unconditionally. @@ -90,6 +109,9 @@ def setup_build_environment(self, env): env.set("SUPERLU_DIR", self.spec["superlu"].prefix) env.set("TRIANGLE_DIR", self.spec["triangle"].prefix) env.set("NCURSES_DIR", self.spec["ncurses"].prefix) + # Needed unconditionally now (config/default.py's get_flags('metis') + # feeds superluWrappers'/csmoothers' link line), not just +scorec. + env.set("METIS_DIR", self.spec["metis"].prefix) if self.spec.satisfies("+scorec"): env.set("SCOREC_DIR", self.spec["pumi"].prefix) From 62701cb6fa32cd511ae6f2dc96dfa77e14cb82cb Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:45:06 +0200 Subject: [PATCH 08/28] py-proteus: build petsc with +hypre+superlu-dist to match other install paths The --download-proteus/HPC and pip paths both configure PETSc with --download-hypre --download-superlu_dist; this Spack recipe's plain depends_on("petsc+mpi") left both disabled by default, silently narrowing which of proteus's solver code paths actually get exercised rather than producing an obvious failure. Confirmed via two full pytest runs against a real `spack install py-proteus+scorec` build: ~hypre~superlu-dist showed 35 failed/387 passed (mostly AMG-based tests -- test_operator_shells, test_stokesDC_TH_2D, test_bochev_pressure_stabilization, the amg_* cases in test_nse_RANS2P_step -- plus a few needing a parallel direct solver); rebuilding with +hypre+superlu-dist brought that down to 17 failed/405 passed, closing every one of the AMG/operator-shell failures. The remaining 17 (vs. the 6 known pre-existing failures other paths show) look like real numerical differences from this Spack build's specific hypre@3.1.0/superlu-dist@9.2.1 versions rather than a missing capability, and haven't been chased further. --- .../spack_repo/builtin/packages/py_proteus/package.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 518dd4fa387..c2869ffa945 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -57,7 +57,15 @@ class PyProteus(PythonPackage): depends_on("py-h5py+mpi", type=("build", "run")) depends_on("mpi") - depends_on("petsc+mpi") + # +hypre+superlu-dist to match the --download-proteus/HPC and pip paths' + # own PETSc configure flags (--download-hypre --download-superlu_dist). + # Without them, a noticeable slice of the solver-heavy test suite fails + # outright (AMG-based tests, parallel-direct-solve tests) rather than + # just running slower/differently -- confirmed via a real + # `spack install py-proteus+scorec` build and full pytest run: 35 failed + # (mostly AMG/solver tests) vs. the 6 known pre-existing failures other + # install paths show. + depends_on("petsc+mpi+hypre+superlu-dist") depends_on("hdf5+mpi+hl") # config/default.py hard-codes '-lopenblas' on Linux. depends_on("openblas") From bee21a2dc95792dc5ddc819e4afcc7bf22c276a2 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Thu, 13 Aug 2026 10:59:38 -0500 Subject: [PATCH 09/28] pumi: patch the _vert_id int/long SCOREC bug; triangle: fix showme's X11 paths pumi: mds/apfMDS.cc's getFaceIdInRegion()/getEdgeIdInFace() helpers read the "_vert_id" mesh tag (created as an 8-byte long via createLongTag) with getIntTag() into a 4-byte int -- SCOREC's generic tag storage does an unconditional memcpy(dest, storage, tag->bytes) with no type/size check, so every call is an 8-byte-into-4-byte stack buffer overflow. Confirmed via gdb on aarch64 (petsc/download-proteus-support session): crashes every PUMI-mesh-generation test through reconstructFromProteus2 -> derive2DMdlFromManifold. Already fixed for PETSc's own --download-scorec via scorec.py in the gitlab.com/cekees/petsc download-proteus-support fork; this is the Spack-side equivalent so py-proteus+scorec gets the same fix, since Spack's pumi recipe clones pristine upstream SCOREC/core independently. Also replaces the silent "return 12; // Should give segmentation fault" fallback with an explicit assertion. triangle: showme's build hardcoded CSWITCHES to a legacy -I/usr/X11R6/include path that doesn't exist when X11 comes from Spack's own libx11 dependency -- the generic build system used here doesn't auto-inject dependency include/lib paths the way Autotools/CMakePackage do. Wire spec["libx11"]/spec["xproto"] prefixes into CSWITCHES explicitly instead (Xlib.h #includes X11/X.h, which comes from xproto, only pulled in transitively before this fix -- add a direct build dep on it too), and apply on every platform rather than only Darwin, keeping -DLINUX on Linux where it belongs. --- .../builtin/packages/pumi/package.py | 117 ++++++++++++++++++ .../builtin/packages/triangle/package.py | 54 +++++--- 2 files changed, 152 insertions(+), 19 deletions(-) diff --git a/repos/spack_repo/builtin/packages/pumi/package.py b/repos/spack_repo/builtin/packages/pumi/package.py index aa630a18aeb..479f5b8479a 100644 --- a/repos/spack_repo/builtin/packages/pumi/package.py +++ b/repos/spack_repo/builtin/packages/pumi/package.py @@ -86,6 +86,123 @@ class Pumi(CMakePackage): depends_on("simmetrix-simmodsuite" + simkernels, when="simmodsuite=kernels") depends_on("simmetrix-simmodsuite" + simfull, when="simmodsuite=full") + def patch(self): + '''mds/apfMDS.cc: getFaceIdInRegion() and getEdgeIdInFace() (helpers used by + apf::deriveMdlFromManifold()/apf::derive2DMdlFromManifold(), called from + proteus's MeshAdaptPUMIDrvr::reconstructFromProteus2()) look up the + "_vert_id" tag via mesh->findTag("_vert_id") and read it with + getIntTag() into a 4-byte int/int[2]. But "_vert_id" is created a few + lines above (and identically in the sibling deriveMdlFromManifold) via + mesh->createLongTag("_vert_id", 1) -- an 8-byte long. SCOREC's generic + tag storage (MeshMDS::getTag) does an unconditional + memcpy(dest, storage, tag->bytes) with no type/size check, so every + call here is an 8-byte-into-4-byte stack buffer overflow -- confirmed + via gdb on aarch64 (petsc/download-proteus-support session, + 2026-08-01): this exact bug crashes every PUMI-mesh-generation test + that goes through this code path (reconstructFromProteus2 -> + derive2DMdlFromManifold), reproducing here too as a glibc + stack-protector "*** buffer overflow detected ***" abort inside + derive2DMdlFromManifold when this same pumi is built via Spack rather + than PETSc's own --download-scorec (which already carries this fix via + config/BuildSystem/config/packages/scorec.py in the + gitlab.com/cekees/petsc download-proteus-support fork -- this file is + the Spack-side equivalent so py-proteus+scorec gets the same fix). + Fixed by matching the tag's actual type (getLongTag/long, not + getIntTag/int) in both helpers, plus replacing the silent + "return 12; // Should give segmentation fault" fallback (apf::Downward + is a fixed MeshEntity*[12]; indexing it with 12 is UB regardless of + what causes the "no match" case) with an explicit assertion so a + genuinely unmatched vertex/edge aborts loudly at the fault site + instead of silently indexing one past the array.''' + apfmds_cc = join_path(self.stage.source_path, 'mds', 'apfMDS.cc') + with open(apfmds_cc, 'r') as f: + content = f.read() + orig = content + content = content.replace( + 'apf::Downward verts;\n' + ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' + ' int vID;\n' + ' mesh->getDownward(region, 0, verts);\n' + ' // Go through all vertices. What vertex is not on the face can be used to determine the face id.\n' + ' // TODO: Good way to assert that the rest of the 3 actually exist?\n' + ' mesh->getIntTag(verts[0], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 2;\n' + ' mesh->getIntTag(verts[1], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 3;\n' + ' mesh->getIntTag(verts[2], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 1;\n' + ' mesh->getIntTag(verts[3], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 0;\n' + ' return 12; // Should give segmentation fault\n' + '}', + 'apf::Downward verts;\n' + ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' + ' PCU_ALWAYS_ASSERT(mesh->getTagType(vIDTag) == Mesh::LONG);\n' + ' long vID;\n' + ' mesh->getDownward(region, 0, verts);\n' + ' // Go through all vertices. What vertex is not on the face can be used to determine the face id.\n' + ' // TODO: Good way to assert that the rest of the 3 actually exist?\n' + ' mesh->getLongTag(verts[0], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 2;\n' + ' mesh->getLongTag(verts[1], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 3;\n' + ' mesh->getLongTag(verts[2], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 1;\n' + ' mesh->getLongTag(verts[3], vIDTag, &vID);\n' + ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' + ' return 0;\n' + ' PCU_ALWAYS_ASSERT_VERBOSE(false, "getFaceIdInRegion: no matching vertex found");\n' + ' return 12; // unreachable\n' + '}') + content = content.replace( + 'apf::Downward verts, edges;\n' + ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' + ' int vID[2], eID;\n' + ' mesh->getDownward(face, 1, edges);\n' + ' for (eID = 0; eID < 3; ++eID) {\n' + ' mesh->getDownward(edges[eID], 0, verts);\n' + ' mesh->getIntTag(verts[0], vIDTag, &vID[0]);\n' + ' mesh->getIntTag(verts[1], vIDTag, &vID[1]);\n' + ' if((vID[0] == bedge_data[2] && vID[1] == bedge_data[3]) ||\n' + ' (vID[0] == bedge_data[3] && vID[1] == bedge_data[2])) {\n' + ' return eID;\n' + ' }\n' + ' }\n' + '\n' + ' return 12; // Should give segmentation fault\n' + '}', + 'apf::Downward verts, edges;\n' + ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' + ' PCU_ALWAYS_ASSERT(mesh->getTagType(vIDTag) == Mesh::LONG);\n' + ' long vID[2];\n' + ' int eID;\n' + ' mesh->getDownward(face, 1, edges);\n' + ' for (eID = 0; eID < 3; ++eID) {\n' + ' mesh->getDownward(edges[eID], 0, verts);\n' + ' mesh->getLongTag(verts[0], vIDTag, &vID[0]);\n' + ' mesh->getLongTag(verts[1], vIDTag, &vID[1]);\n' + ' if((vID[0] == bedge_data[2] && vID[1] == bedge_data[3]) ||\n' + ' (vID[0] == bedge_data[3] && vID[1] == bedge_data[2])) {\n' + ' return eID;\n' + ' }\n' + ' }\n' + '\n' + ' PCU_ALWAYS_ASSERT_VERBOSE(false, "getEdgeIdInFace: no matching edge found");\n' + ' return 12; // unreachable\n' + '}') + if content == orig: + tty.warn('pumi patch(): apfMDS.cc _vert_id int/long fix found nothing to replace -- upstream source may have changed, check getFaceIdInRegion/getEdgeIdInFace by hand') + else: + with open(apfmds_cc, 'w') as f: + f.write(content) + def cmake_args(self): spec = self.spec diff --git a/repos/spack_repo/builtin/packages/triangle/package.py b/repos/spack_repo/builtin/packages/triangle/package.py index 32a76eda457..9dc1bbda32f 100644 --- a/repos/spack_repo/builtin/packages/triangle/package.py +++ b/repos/spack_repo/builtin/packages/triangle/package.py @@ -23,28 +23,44 @@ class Triangle(Package): version("1.6", sha256="1766327add038495fa3499e9b7cc642179229750f7201b94f8e1b7bee76f8480") - depends_on("libx11", type="link") + depends_on("libx11", type=("build", "link")) + # Xlib.h itself #includes X11/X.h (protocol constants), which libx11 + # doesn't re-export -- it comes from xproto, already pulled in + # transitively as one of libx11's own build deps, but that isn't enough + # to put its prefix on this package's own include path below; depend on + # it directly so spec["xproto"] is guaranteed available here too + # (confirmed needed via a real build: showme failed with "X11/X.h: No + # such file or directory" once Xlib.h's own location was fixed). + depends_on("xproto", type="build") depends_on("gmake", type="build") def install(self, spec, prefix): - # triangle 1.6's own makefile hardcodes CSWITCHES with -DLINUX - # unconditionally, regardless of the actual build platform. triangle.c - # only consults that macro (alongside the mutually-exclusive -DCPU86) - # to twiddle the legacy x87 FPU's precision-control register on old - # x86 hardware -- irrelevant on Apple Silicon (no x87 unit) and - # unnecessary on modern x86_64 (doubles use SSE by default), but the - # macro also gates an unconditional `#include `, a - # glibc/Linux-only header that doesn't exist on macOS at all. Building - # with the stock makefile there fails outright with "fatal error: - # 'fpu_control.h' file not found". Override CSWITCHES on Darwin to - # drop -DLINUX (and skip the header/precision tweak entirely, since - # neither alternative macro applies here); Linux builds are - # unaffected and keep the upstream makefile's own default. - cswitches = "-O -I/usr/X11R6/include -L/usr/X11R6/lib" - if spec.satisfies("platform=darwin"): - make("CSWITCHES=" + cswitches) - else: - make() + # triangle 1.6's own makefile hardcodes CSWITCHES to + # "-O -DLINUX -I/usr/X11R6/include -L/usr/X11R6/lib" unconditionally, + # regardless of the actual build platform, and CSWITCHES must be + # overridden on every platform (not just Darwin) for two independent + # reasons: + # - showme.c #includes X11/Xlib.h directly, and the hardcoded + # -I/usr/X11R6/include is a legacy path that doesn't exist once + # X11 comes from Spack's own libx11 dependency rather than a + # system install (confirmed via a real build: showme failed with + # "X11/Xlib.h: No such file or directory" even with libx11 built + # as a dependency above -- the generic build system used here + # doesn't auto-inject dependency include/lib paths the way + # AutotoolsPackage/CMakePackage do, so they must be wired up + # explicitly here). + # - -DLINUX gates an unconditional `#include ` in + # triangle.c (used to twiddle the legacy x87 FPU's + # precision-control register on old x86 hardware -- irrelevant on + # Apple Silicon, unnecessary on modern x86_64 where doubles use + # SSE by default) via a glibc/Linux-only header that doesn't + # exist on macOS at all; keep it on Linux, drop it on Darwin. + x11 = spec["libx11"].prefix + xproto = spec["xproto"].prefix + cswitches = "-O -I{0} -I{1} -L{2}".format(x11.include, xproto.include, x11.lib) + if not spec.satisfies("platform=darwin"): + cswitches = "-DLINUX " + cswitches + make("CSWITCHES=" + cswitches) mkdirp(prefix.bin) install("triangle", prefix.bin) From 32a96377182a1ea2ef4641987a94294a14494be2 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:28:26 -0500 Subject: [PATCH 10/28] py_proteus: track main instead of stale torino_narwhal; pumi: add 4.1.0 py_proteus's only version() entry was still torino_narwhal, a branch proteus's main has since fully absorbed (plus a Chrono 10.0.0/SCOREC 4.1.0 upgrade and a PCU API port for MeshAdaptPUMI, all validated this session across the pip, HPC/PETSc BuildSystem, conda, and Spack install pathways). Also drops the now-moot Git LFS bandwidth-budget warning, since main dropped LFS entirely for its test comparison data. pumi had no version past 2.2.9, predating the SCOREC/core >=3.0.0 PCU API refactor proteus's main branch now requires (a new PCU_C.h header carries the C-linkage declarations) -- every version this recipe could offer failed proteus's build with "PCU_C.h: No such file or directory". Added the real upstream v4.1.0 tag (commit resolved via git ls-remote), matching the same version petsc's own scorec.py was independently bumped to for the HPC/PETSc BuildSystem pathway. --- .../builtin/packages/pumi/package.py | 10 +++++++ .../builtin/packages/py_proteus/package.py | 28 ++++++++++--------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/repos/spack_repo/builtin/packages/pumi/package.py b/repos/spack_repo/builtin/packages/pumi/package.py index 479f5b8479a..7391fffd785 100644 --- a/repos/spack_repo/builtin/packages/pumi/package.py +++ b/repos/spack_repo/builtin/packages/pumi/package.py @@ -33,6 +33,16 @@ class Pumi(CMakePackage): # scorec/core develop branch and we prefer not to expose spack users # to the added instability. version("master", submodules=True, branch="master") + # scorec/core >=3.0.0 replaced PCU's global mutable state with an + # explicit pcu::PCU object (every PCU_* free function now takes a PCU_t + # handle, and a new PCU_C.h header carries the C-linkage declarations); + # proteus's main branch requires this API as of its own PCU port + # (proteus/MeshAdaptPUMI/*), so pin a tagged release past that + # refactor -- 2.2.9 (and everything below it here) predates it entirely + # and fails proteus's build with "PCU_C.h: No such file or directory". + version( + "4.1.0", submodules=True, commit="a8e3aef58bfe86790782c4ae5e5c1bb5f232ff30" + ) # tag 4.1.0 version( "2.2.9", submodules=True, commit="f87525cae7597322edfb2ccf1c7d4437402d9481" ) # tag 2.2.9 diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index c2869ffa945..69c1e8ec8de 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -13,26 +13,28 @@ class PyProteus(PythonPackage): around PETSc for parallel linear/nonlinear solves.""" homepage = "http://proteustoolkit.org" - # Tracks cekees/proteus's torino_narwhal branch rather than erdc/proteus - # releases: upstream unconditionally requires a Chrono install. - # torino_narwhal makes Chrono and SCOREC independently optional. + # Tracks cekees/proteus's main branch rather than erdc/proteus releases: + # upstream unconditionally requires a Chrono install, but main (like the + # torino_narwhal branch this used to track, now merged into main and + # fully superseded) makes Chrono and SCOREC independently optional. # - # NOTE: github.com/cekees/proteus's fork-network LFS bandwidth budget is - # billed against upstream erdc/proteus, not cekees, and can be exhausted - # independent of anything the cekees fork owner does -- a fresh clone's - # `git checkout` then fails during LFS smudge ("git checkout --quiet - # ...: exit status 128"). If you hit that, point this at a local, - # already-cloned checkout instead (`git = "file:///path/to/checkout"`) - # as a temporary local edit -- Spack has no per-invocation override for - # a version's own git URL (confirmed: `git=` is not a valid spec - # variant here). + # NOTE: this used to warn about github.com/cekees/proteus's fork-network + # Git LFS bandwidth budget (billed against upstream erdc/proteus, not + # cekees, and exhaustible independent of anything the cekees fork owner + # does) -- moot as of main dropping LFS entirely for its test comparison + # data (commit c75eb6c9, "Stop tracking test comparison data via Git + # LFS"). If a similar external-quota issue ever recurs for some other + # reason, point this at a local, already-cloned checkout instead + # (`git = "file:///path/to/checkout"`) as a temporary local edit -- + # Spack has no per-invocation override for a version's own git URL + # (confirmed: `git=` is not a valid spec variant here). git = "https://github.com/cekees/proteus.git" maintainers("cekees") license("MIT") - version("torino_narwhal", branch="torino_narwhal") + version("main", branch="main") # Chrono (pychrono) has no upstream Spack package and stays disabled. # SCOREC/PUMI does (`pumi`); this variant wires it in instead of From 41c1e99f6b2e8283eaeb8c3afae0e28e1dace761 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:26:12 +0200 Subject: [PATCH 11/28] petsc: add +superlu variant (sequential SuperLU, distinct from +superlu-dist) PETSc's "superlu" and "superlu_dist" MatSolverTypes are separate libraries/options -- this package only had a variant for the parallel one (superlu-dist). Some proteus tests (elliptic_redist/RDLS3P, periodic) explicitly request pc_factor_mat_solver_package='superlu', which failed outright ("Could not locate solver type superlu for factorization type LU ... Perhaps you must ./configure with --download-superlu") with no way to enable it via this recipe at all. Added variant("superlu", ...), depends_on("superlu", when="+superlu"), a conflicts("+superlu+int64", ...) mirroring SuperLU's own upstream 64-bit-integer limitation, and a ("superlu", "superlu", True, True) entry in the existing --with--dir configure-args loop (which already handles suite-sparse/superlu-dist identically). Confirmed via a real `spack install py-proteus` build and test run: 6 tests failed on exactly this error before this variant existed. --- .../spack_repo/builtin/packages/petsc/package.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index f1d49640167..87adeda9510 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -136,6 +136,17 @@ class Petsc(Package, CudaPackage, ROCmPackage): when="+fortran", description="Activates support for superlu-dist (only parallel)", ) + # Sequential SuperLU (distinct package from superlu-dist above): needed + # for PETSc's "superlu" MatSolverType, requested explicitly by some + # PETSc/proteus code (e.g. proteus's RDLS3P/periodic tests set + # pc_factor_mat_solver_package='superlu'). Without this, PETSc raises + # "Could not locate solver type superlu for factorization type LU" even + # though superlu-dist is enabled -- the two MatSolverTypes aren't + # interchangeable (confirmed via a real `spack install py-proteus` test + # run: 6 tests failed on exactly that error before this variant existed). + variant( + "superlu", default=False, description="Activates support for superlu (sequential)" + ) variant("strumpack", default=False, description="Activates support for Strumpack") variant( "scalapack", default=False, when="+fortran", description="Activates support for Scalapack" @@ -359,6 +370,10 @@ class Petsc(Package, CudaPackage, ROCmPackage): depends_on("mkl", when="+mkl-pardiso") depends_on("fftw+mpi", when="+fftw+mpi") depends_on("suite-sparse", when="+suite-sparse") + depends_on("superlu", when="+superlu") + # SuperLU (sequential) has no 64-bit integer support -- use superlu-dist + # for that instead (its own package.py already declares that split). + conflicts("+superlu+int64", msg="SuperLU has no support for 64-bit integers, use superlu-dist instead") depends_on("libx11", when="+X") depends_on("mpfr", when="+mpfr") depends_on("gmp", when="+mpfr") @@ -542,6 +557,7 @@ def configure_options(self): ("kokkos", "kokkos", False, False), ("kokkos-kernels", "kokkos-kernels", False, False), ("superlu-dist", "superlu_dist", True, True), + ("superlu", "superlu", True, True), ("scotch", "ptscotch", True, True), ( "suite-sparse:umfpack,klu,cholmod,btf,ccolamd,colamd,camd,amd," From d00426cd77c15e0bc8dbdd95f95bf594b5331daa Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:30:14 +0200 Subject: [PATCH 12/28] py-proteus: add +superlu/+tetgen, pin pumi@4.1.0 explicitly, fix MPI header detection Three real gaps found via a full `spack install py-proteus+scorec` test run against main, not yet covered by the pumi@4.1.0/track-main work already on this branch: - petsc dependency gains +superlu (distinct from +superlu-dist -- see petsc's own package.py, which just added the variant) and +tetgen, matching the pip/HPC pathways' own PETSc configure flags. Without them, RDLS3P/periodic tests (pc_factor_mat_solver_package='superlu') and PETSc's DMPlex tetgen-file-reading support (ci/test_meshPartitionFromTetgenFiles.py) fail outright. - Added depends_on("tetgen") directly too: proteus's own mesh-generation helpers (MeshTools.buildReferenceSimplex) shell out to the standalone tetgen CLI, same as the existing triangle dependency's role. test_generate_reference_simplex failed with the CLI missing before this. - pumi dependency pinned explicitly to @4.1.0 (was left unpinned, relying on Spack's default latest-version preference to avoid the still-offered 2.2.9) -- explicit rather than implicit, since main's MeshAdaptPUMI source only works against the newer PCU API 4.1.0 provides. Also fixed a real MPI-header-detection gap: config/default.py's get_flags('mpi') looks for $MPI_DIR/include/mpi.h, true for a normal self-contained MPI prefix but not for Debian/Ubuntu's system mpich package, which Spack's external-package detection can pick up with a generic /usr prefix that has no include/mpi.h at all -- Debian nests it under a multiarch-qualified path instead (/usr/lib//mpich/include/mpi.h). Added _mpi_dir(), which prefers the plain prefix/include/mpi.h layout (true for any Spack-built MPI) and only searches for Debian's nested layout as a fallback. With all of the above (plus the pumi/triangle fixes already on this branch), `spack install -y py-proteus+scorec` goes from 26 failed to 9 failed, with every remaining failure already understood and documented (numeric-mismatch findings needing domain review, floating-point noise, gmsh CLI missing). --- .../builtin/packages/py_proteus/package.py | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 69c1e8ec8de..10e4bfb7f43 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -2,6 +2,9 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import glob +import os + from spack_repo.builtin.build_systems.python import PythonPackage from spack.package import * @@ -59,15 +62,27 @@ class PyProteus(PythonPackage): depends_on("py-h5py+mpi", type=("build", "run")) depends_on("mpi") - # +hypre+superlu-dist to match the --download-proteus/HPC and pip paths' - # own PETSc configure flags (--download-hypre --download-superlu_dist). - # Without them, a noticeable slice of the solver-heavy test suite fails - # outright (AMG-based tests, parallel-direct-solve tests) rather than - # just running slower/differently -- confirmed via a real - # `spack install py-proteus+scorec` build and full pytest run: 35 failed - # (mostly AMG/solver tests) vs. the 6 known pre-existing failures other - # install paths show. - depends_on("petsc+mpi+hypre+superlu-dist") + # +hypre+superlu-dist+superlu to match the --download-proteus/HPC and pip + # paths' own PETSc configure flags (--download-hypre --download-superlu + # --download-superlu_dist). Without hypre/superlu-dist, a noticeable + # slice of the solver-heavy test suite fails outright (AMG-based tests, + # parallel-direct-solve tests) rather than just running slower/ + # differently -- confirmed via a real `spack install py-proteus+scorec` + # build and full pytest run: 35 failed (mostly AMG/solver tests) vs. the + # 6 known pre-existing failures other install paths show. +superlu + # (distinct from +superlu-dist -- PETSc's "superlu" and "superlu_dist" + # MatSolverTypes aren't interchangeable) is needed too: some tests + # (RDLS3P, periodic) explicitly request pc_factor_mat_solver_package= + # 'superlu'; this builtin petsc package had no way to enable that at all + # until this session added the +superlu variant (see petsc's own + # package.py). + # +tetgen matches the pip/HPC pathways' PETSc configure (--download- + # tetgen): PETSc's DMPlex tetgen-file reader (used by the ci/test_ + # meshPartitionFromTetgenFiles.py tests, which partition pre-generated + # .node/.ele/.face files rather than shelling out to the tetgen CLI) + # needs PETSc itself built with tetgen support, not just the standalone + # CLI on PATH. + depends_on("petsc+mpi+hypre+superlu-dist+superlu+tetgen") depends_on("hdf5+mpi+hl") # config/default.py hard-codes '-lopenblas' on Linux. depends_on("openblas") @@ -82,6 +97,15 @@ class PyProteus(PythonPackage): depends_on("metis") depends_on("superlu") depends_on("triangle") + # proteus's own mesh-generation helpers (MeshTools.buildReferenceSimplex, + # among others) shell out to the standalone "tetgen" CLI directly, same + # as with "triangle" above; the pip/HPC pathways' own PETSc configure + # line also builds tetgen (--download-tetgen --download-tetgen-build- + # exec=1) for the same reason. Confirmed via a real + # `spack install py-proteus+scorec` test run: test_generate_reference_ + # simplex and both TestPoissonTetgen tests failed with the CLI missing + # from PATH / PETSc's own tetgen-backed DMPlex support disabled. + depends_on("tetgen") # proteus.fenton.Fenton links ncurses unconditionally. depends_on("ncurses") @@ -89,7 +113,12 @@ class PyProteus(PythonPackage): # needs its own *_DIR in setup_build_environment. # +shared: pumi's static libs aren't built with -fPIC and can't link # into the shared MeshAdaptPUMI.MeshAdapt extension. - depends_on("pumi+zoltan+shared", when="+scorec") + # @4.1.0: proteus's own MeshAdaptPUMI source (main) requires the newer + # PCU API (pcu/PCU_C.h) that pumi's older versions predate -- pinned + # explicitly rather than relying on Spack's default latest-version + # preference, since pumi still offers 2.2.9 too (see pumi's own + # package.py for the full explanation). + depends_on("pumi@4.1.0+zoltan+shared", when="+scorec") # ~fortran: unused here, and zoltan's Fortran interface leaves # unresolved libgfortran symbols in libzoltan.so on this toolchain. depends_on("zoltan+parmetis~fortran", when="+scorec") @@ -105,6 +134,36 @@ class PyProteus(PythonPackage): depends_on("xtensor-python@0.28.0:") depends_on("xtl") + def _mpi_dir(self): + # config/default.py's get_flags('mpi') looks for $MPI_DIR/include/ + # mpi.h -- true for a normal, self-contained MPI prefix (e.g. a + # Spack-built mpich/openmpi), but not for Debian/Ubuntu's system + # mpich package: when Spack's external-package detection picks that + # up (as it does whenever `spack external find` runs on a machine + # with mpich already apt-installed), self.spec["mpi"].prefix is the + # generic /usr, which has no include/mpi.h at all -- Debian's mpich + # package instead nests everything under a per-implementation, + # multiarch-qualified directory (/usr/lib//mpich/include/ + # mpi.h) so it can coexist with an openmpi package's own headers at + # the same prefix. Confirmed directly on this machine: proteus's + # pip-install pathway hit the identical gap and needed the same + # kind of explicit redirect. Prefer the plain prefix/include/mpi.h + # layout when it exists (true for every Spack-built MPI), and only + # fall back to searching for Debian's nested layout otherwise. + mpi_prefix = self.spec["mpi"].prefix + if os.path.isfile(os.path.join(mpi_prefix, "include", "mpi.h")): + return mpi_prefix + for candidate in glob.glob(os.path.join(mpi_prefix, "lib", "*", "mpich")): + if os.path.isfile(os.path.join(candidate, "include", "mpi.h")): + return candidate + for candidate in glob.glob(os.path.join(mpi_prefix, "lib", "*", "openmpi")): + if os.path.isfile(os.path.join(candidate, "include", "mpi.h")): + return candidate + # No known layout matched -- fall back to the plain prefix so the + # resulting error (if any) still points at a real, inspectable path + # rather than silently returning something clearly wrong. + return mpi_prefix + def setup_build_environment(self, env): env.set("PROTEUS_SKIP_CHRONO", "1") if self.spec.satisfies("~scorec"): @@ -112,7 +171,7 @@ def setup_build_environment(self, env): # config/default.py's get_flags() reads a _DIR per dependency. env.set("PETSC_DIR", self.spec["petsc"].prefix) - env.set("MPI_DIR", self.spec["mpi"].prefix) + env.set("MPI_DIR", self._mpi_dir()) env.set("HDF5_DIR", self.spec["hdf5"].prefix) env.set("BLAS_DIR", self.spec["openblas"].prefix) env.set("LAPACK_DIR", self.spec["openblas"].prefix) From 1034532f159e8c6a4459ed47317dd6d007ea5d76 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:40:12 +0200 Subject: [PATCH 13/28] triangle: fix K&R-style implicit-declaration build failure on modern GCC The showme/X11 CSWITCHES fix already on this branch builds cleanly on whatever GCC version/platform it was verified against, but fails outright on GCC 15.2.0 (confirmed via a real `spack install py-proteus+scorec` build here): triangle.c/showme.c are K&R-style C (old-style function definitions, no prototypes in scope at call sites), and a sufficiently modern GCC treats a call to an as-yet-undeclared function as returning int with no parameters, then reports a hard "too many arguments to function 'readline'/'findfield'/ 'load_image'" error once it later reaches that function's own (parameter-bearing) K&R definition, rather than the warning older defaults gave. PETSc's own Triangle.py package hits the identical failure and works around it the same way -- add -std=gnu17 (old enough to keep implicit declarations as a warning, not an error) to CSWITCHES unconditionally. Verified: `spack install py-proteus+scorec` now builds triangle (showme included) and the full dependent chain cleanly. --- .../builtin/packages/triangle/package.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/triangle/package.py b/repos/spack_repo/builtin/packages/triangle/package.py index 9dc1bbda32f..e76962a1456 100644 --- a/repos/spack_repo/builtin/packages/triangle/package.py +++ b/repos/spack_repo/builtin/packages/triangle/package.py @@ -55,9 +55,23 @@ def install(self, spec, prefix): # Apple Silicon, unnecessary on modern x86_64 where doubles use # SSE by default) via a glibc/Linux-only header that doesn't # exist on macOS at all; keep it on Linux, drop it on Darwin. + # - triangle.c/showme.c are K&R-style C (old-style function + # definitions, no prototypes in scope at call sites) -- harmless + # under GCC's old implicit-int/implicit-declaration defaults, but + # a sufficiently modern GCC (confirmed reproducing on 15.2.0; + # apparently not triggered on whatever GCC version/platform this + # fix was originally verified against) defaults to a C standard + # that treats a call to an as-yet-undeclared function as + # returning int with no parameters, then reports a hard "too many + # arguments" error once it reaches that function's own + # (parameter-bearing) K&R definition later in the file, rather + # than the warning older defaults gave. PETSc's own Triangle.py + # package hits the identical failure and works around it exactly + # this way -- add -std=gnu17 (old enough to keep implicit + # declarations as a warning, not an error) unconditionally. x11 = spec["libx11"].prefix xproto = spec["xproto"].prefix - cswitches = "-O -I{0} -I{1} -L{2}".format(x11.include, xproto.include, x11.lib) + cswitches = "-O -std=gnu17 -I{0} -I{1} -L{2}".format(x11.include, xproto.include, x11.lib) if not spec.satisfies("platform=darwin"): cswitches = "-DLINUX " + cswitches make("CSWITCHES=" + cswitches) From 9fd3d46f9cdd6732fe56d99855f5d067de26935b Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:24:30 +0200 Subject: [PATCH 14/28] Cleaned up wordy comments and proteus-pumi dep --- .../builtin/packages/petsc/package.py | 10 -- .../builtin/packages/pumi/package.py | 127 +--------------- .../builtin/packages/py_proteus/package.py | 137 +++--------------- .../builtin/packages/triangle/package.py | 52 ------- .../builtin/packages/zoltan/package.py | 26 ---- 5 files changed, 22 insertions(+), 330 deletions(-) diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index 4c1be775328..036847988a2 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -136,14 +136,6 @@ class Petsc(Package, CudaPackage, ROCmPackage): when="+fortran", description="Activates support for superlu-dist (only parallel)", ) - # Sequential SuperLU (distinct package from superlu-dist above): needed - # for PETSc's "superlu" MatSolverType, requested explicitly by some - # PETSc/proteus code (e.g. proteus's RDLS3P/periodic tests set - # pc_factor_mat_solver_package='superlu'). Without this, PETSc raises - # "Could not locate solver type superlu for factorization type LU" even - # though superlu-dist is enabled -- the two MatSolverTypes aren't - # interchangeable (confirmed via a real `spack install py-proteus` test - # run: 6 tests failed on exactly that error before this variant existed). variant( "superlu", default=False, description="Activates support for superlu (sequential)" ) @@ -371,8 +363,6 @@ class Petsc(Package, CudaPackage, ROCmPackage): depends_on("fftw+mpi", when="+fftw+mpi") depends_on("suite-sparse", when="+suite-sparse") depends_on("superlu", when="+superlu") - # SuperLU (sequential) has no 64-bit integer support -- use superlu-dist - # for that instead (its own package.py already declares that split). conflicts("+superlu+int64", msg="SuperLU has no support for 64-bit integers, use superlu-dist instead") depends_on("libx11", when="+X") depends_on("mpfr", when="+mpfr") diff --git a/repos/spack_repo/builtin/packages/pumi/package.py b/repos/spack_repo/builtin/packages/pumi/package.py index 7391fffd785..0c95e893f02 100644 --- a/repos/spack_repo/builtin/packages/pumi/package.py +++ b/repos/spack_repo/builtin/packages/pumi/package.py @@ -33,13 +33,9 @@ class Pumi(CMakePackage): # scorec/core develop branch and we prefer not to expose spack users # to the added instability. version("master", submodules=True, branch="master") - # scorec/core >=3.0.0 replaced PCU's global mutable state with an - # explicit pcu::PCU object (every PCU_* free function now takes a PCU_t - # handle, and a new PCU_C.h header carries the C-linkage declarations); - # proteus's main branch requires this API as of its own PCU port - # (proteus/MeshAdaptPUMI/*), so pin a tagged release past that - # refactor -- 2.2.9 (and everything below it here) predates it entirely - # and fails proteus's build with "PCU_C.h: No such file or directory". + version( + "4.2.0", submodules=True, commit="a40922de30f09af63a0251a0d53f95c6cadd9199" + ) # tag 4.2.0 version( "4.1.0", submodules=True, commit="a8e3aef58bfe86790782c4ae5e5c1bb5f232ff30" ) # tag 4.1.0 @@ -96,123 +92,6 @@ class Pumi(CMakePackage): depends_on("simmetrix-simmodsuite" + simkernels, when="simmodsuite=kernels") depends_on("simmetrix-simmodsuite" + simfull, when="simmodsuite=full") - def patch(self): - '''mds/apfMDS.cc: getFaceIdInRegion() and getEdgeIdInFace() (helpers used by - apf::deriveMdlFromManifold()/apf::derive2DMdlFromManifold(), called from - proteus's MeshAdaptPUMIDrvr::reconstructFromProteus2()) look up the - "_vert_id" tag via mesh->findTag("_vert_id") and read it with - getIntTag() into a 4-byte int/int[2]. But "_vert_id" is created a few - lines above (and identically in the sibling deriveMdlFromManifold) via - mesh->createLongTag("_vert_id", 1) -- an 8-byte long. SCOREC's generic - tag storage (MeshMDS::getTag) does an unconditional - memcpy(dest, storage, tag->bytes) with no type/size check, so every - call here is an 8-byte-into-4-byte stack buffer overflow -- confirmed - via gdb on aarch64 (petsc/download-proteus-support session, - 2026-08-01): this exact bug crashes every PUMI-mesh-generation test - that goes through this code path (reconstructFromProteus2 -> - derive2DMdlFromManifold), reproducing here too as a glibc - stack-protector "*** buffer overflow detected ***" abort inside - derive2DMdlFromManifold when this same pumi is built via Spack rather - than PETSc's own --download-scorec (which already carries this fix via - config/BuildSystem/config/packages/scorec.py in the - gitlab.com/cekees/petsc download-proteus-support fork -- this file is - the Spack-side equivalent so py-proteus+scorec gets the same fix). - Fixed by matching the tag's actual type (getLongTag/long, not - getIntTag/int) in both helpers, plus replacing the silent - "return 12; // Should give segmentation fault" fallback (apf::Downward - is a fixed MeshEntity*[12]; indexing it with 12 is UB regardless of - what causes the "no match" case) with an explicit assertion so a - genuinely unmatched vertex/edge aborts loudly at the fault site - instead of silently indexing one past the array.''' - apfmds_cc = join_path(self.stage.source_path, 'mds', 'apfMDS.cc') - with open(apfmds_cc, 'r') as f: - content = f.read() - orig = content - content = content.replace( - 'apf::Downward verts;\n' - ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' - ' int vID;\n' - ' mesh->getDownward(region, 0, verts);\n' - ' // Go through all vertices. What vertex is not on the face can be used to determine the face id.\n' - ' // TODO: Good way to assert that the rest of the 3 actually exist?\n' - ' mesh->getIntTag(verts[0], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 2;\n' - ' mesh->getIntTag(verts[1], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 3;\n' - ' mesh->getIntTag(verts[2], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 1;\n' - ' mesh->getIntTag(verts[3], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 0;\n' - ' return 12; // Should give segmentation fault\n' - '}', - 'apf::Downward verts;\n' - ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' - ' PCU_ALWAYS_ASSERT(mesh->getTagType(vIDTag) == Mesh::LONG);\n' - ' long vID;\n' - ' mesh->getDownward(region, 0, verts);\n' - ' // Go through all vertices. What vertex is not on the face can be used to determine the face id.\n' - ' // TODO: Good way to assert that the rest of the 3 actually exist?\n' - ' mesh->getLongTag(verts[0], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 2;\n' - ' mesh->getLongTag(verts[1], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 3;\n' - ' mesh->getLongTag(verts[2], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 1;\n' - ' mesh->getLongTag(verts[3], vIDTag, &vID);\n' - ' if (vID != bface_data[2] && vID != bface_data[3] && vID != bface_data[4])\n' - ' return 0;\n' - ' PCU_ALWAYS_ASSERT_VERBOSE(false, "getFaceIdInRegion: no matching vertex found");\n' - ' return 12; // unreachable\n' - '}') - content = content.replace( - 'apf::Downward verts, edges;\n' - ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' - ' int vID[2], eID;\n' - ' mesh->getDownward(face, 1, edges);\n' - ' for (eID = 0; eID < 3; ++eID) {\n' - ' mesh->getDownward(edges[eID], 0, verts);\n' - ' mesh->getIntTag(verts[0], vIDTag, &vID[0]);\n' - ' mesh->getIntTag(verts[1], vIDTag, &vID[1]);\n' - ' if((vID[0] == bedge_data[2] && vID[1] == bedge_data[3]) ||\n' - ' (vID[0] == bedge_data[3] && vID[1] == bedge_data[2])) {\n' - ' return eID;\n' - ' }\n' - ' }\n' - '\n' - ' return 12; // Should give segmentation fault\n' - '}', - 'apf::Downward verts, edges;\n' - ' apf::MeshTag* vIDTag = mesh->findTag("_vert_id");\n' - ' PCU_ALWAYS_ASSERT(mesh->getTagType(vIDTag) == Mesh::LONG);\n' - ' long vID[2];\n' - ' int eID;\n' - ' mesh->getDownward(face, 1, edges);\n' - ' for (eID = 0; eID < 3; ++eID) {\n' - ' mesh->getDownward(edges[eID], 0, verts);\n' - ' mesh->getLongTag(verts[0], vIDTag, &vID[0]);\n' - ' mesh->getLongTag(verts[1], vIDTag, &vID[1]);\n' - ' if((vID[0] == bedge_data[2] && vID[1] == bedge_data[3]) ||\n' - ' (vID[0] == bedge_data[3] && vID[1] == bedge_data[2])) {\n' - ' return eID;\n' - ' }\n' - ' }\n' - '\n' - ' PCU_ALWAYS_ASSERT_VERBOSE(false, "getEdgeIdInFace: no matching edge found");\n' - ' return 12; // unreachable\n' - '}') - if content == orig: - tty.warn('pumi patch(): apfMDS.cc _vert_id int/long fix found nothing to replace -- upstream source may have changed, check getFaceIdInRegion/getEdgeIdInFace by hand') - else: - with open(apfmds_cc, 'w') as f: - f.write(content) - def cmake_args(self): spec = self.spec diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 10e4bfb7f43..ea0a7d51be1 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -12,25 +12,9 @@ class PyProteus(PythonPackage): """Proteus: Computational Methods and Simulation Toolkit. Python tools - for rapidly developing computer models and numerical methods, built - around PETSc for parallel linear/nonlinear solves.""" + for rapidly developing computer models and numerical methods""" homepage = "http://proteustoolkit.org" - # Tracks cekees/proteus's main branch rather than erdc/proteus releases: - # upstream unconditionally requires a Chrono install, but main (like the - # torino_narwhal branch this used to track, now merged into main and - # fully superseded) makes Chrono and SCOREC independently optional. - # - # NOTE: this used to warn about github.com/cekees/proteus's fork-network - # Git LFS bandwidth budget (billed against upstream erdc/proteus, not - # cekees, and exhaustible independent of anything the cekees fork owner - # does) -- moot as of main dropping LFS entirely for its test comparison - # data (commit c75eb6c9, "Stop tracking test comparison data via Git - # LFS"). If a similar external-quota issue ever recurs for some other - # reason, point this at a local, already-cloned checkout instead - # (`git = "file:///path/to/checkout"`) as a temporary local edit -- - # Spack has no per-invocation override for a version's own git URL - # (confirmed: `git=` is not a valid spec variant here). git = "https://github.com/cekees/proteus.git" maintainers("cekees") @@ -39,117 +23,41 @@ class PyProteus(PythonPackage): version("main", branch="main") - # Chrono (pychrono) has no upstream Spack package and stays disabled. - # SCOREC/PUMI does (`pumi`); this variant wires it in instead of - # always skipping it. - variant("scorec", default=False, description="Enable SCOREC/PUMI mesh adaptation support") + # Chrono (pychrono) currently has no upstream Spack package and stays disabled. + variant("pumi", default=False, description="Enable PUMI mesh adaptation support") depends_on("c", type="build") depends_on("cxx", type="build") - depends_on("fortran", type="build") - + depends_on("fortran", type="build")#remove in future not a direct proteus dep depends_on("python@3.9:", type=("build", "run")) depends_on("py-setuptools@61:", type="build") depends_on("py-cython@3:", type="build") - # pybind11 3.x + xtensor@0.27.1 fails to compile: ambiguous operator*= - # overload in xtensor's xsemantic.hpp. - depends_on("py-pybind11@2.11:2", type="build") - + depends_on("py-pybind11@2.11:2", type="build") #xtensor@0.27.1 *= overload issue depends_on("py-numpy@1.25:", type=("build", "run")) depends_on("py-scipy", type=("build", "run")) depends_on("py-mpi4py", type=("build", "run")) depends_on("py-petsc4py", type=("build", "run")) depends_on("py-h5py+mpi", type=("build", "run")) - depends_on("mpi") - # +hypre+superlu-dist+superlu to match the --download-proteus/HPC and pip - # paths' own PETSc configure flags (--download-hypre --download-superlu - # --download-superlu_dist). Without hypre/superlu-dist, a noticeable - # slice of the solver-heavy test suite fails outright (AMG-based tests, - # parallel-direct-solve tests) rather than just running slower/ - # differently -- confirmed via a real `spack install py-proteus+scorec` - # build and full pytest run: 35 failed (mostly AMG/solver tests) vs. the - # 6 known pre-existing failures other install paths show. +superlu - # (distinct from +superlu-dist -- PETSc's "superlu" and "superlu_dist" - # MatSolverTypes aren't interchangeable) is needed too: some tests - # (RDLS3P, periodic) explicitly request pc_factor_mat_solver_package= - # 'superlu'; this builtin petsc package had no way to enable that at all - # until this session added the +superlu variant (see petsc's own - # package.py). - # +tetgen matches the pip/HPC pathways' PETSc configure (--download- - # tetgen): PETSc's DMPlex tetgen-file reader (used by the ci/test_ - # meshPartitionFromTetgenFiles.py tests, which partition pre-generated - # .node/.ele/.face files rather than shelling out to the tetgen CLI) - # needs PETSc itself built with tetgen support, not just the standalone - # CLI on PATH. - depends_on("petsc+mpi+hypre+superlu-dist+superlu+tetgen") + depends_on("petsc+mpi+hypre+superlu-dist+superlu+tetgen") #how much to lock in? depends_on("hdf5+mpi+hl") - # config/default.py hard-codes '-lopenblas' on Linux. - depends_on("openblas") - # superlu's own library calls METIS_NodeND for fill-reducing ordering, but - # metis was previously only pulled in transitively via parmetis, and only - # when +scorec. Without a direct dependency Spack never adds metis's lib - # dir to the rpath/link-path Spack automatically wires up for direct - # depends_on() packages, so the base (~scorec) build's superluWrappers - # extension fails at import time with "symbol not found in flat - # namespace '_METIS_NodeND'" (confirmed via a real `spack install - # py-proteus` build). - depends_on("metis") + depends_on("openblas")#should relax to generic blas in future + depends_on("metis")#through several dependencies depends_on("superlu") - depends_on("triangle") - # proteus's own mesh-generation helpers (MeshTools.buildReferenceSimplex, - # among others) shell out to the standalone "tetgen" CLI directly, same - # as with "triangle" above; the pip/HPC pathways' own PETSc configure - # line also builds tetgen (--download-tetgen --download-tetgen-build- - # exec=1) for the same reason. Confirmed via a real - # `spack install py-proteus+scorec` test run: test_generate_reference_ - # simplex and both TestPoissonTetgen tests failed with the CLI missing - # from PATH / PETSc's own tetgen-backed DMPlex support disabled. - depends_on("tetgen") - # proteus.fenton.Fenton links ncurses unconditionally. - depends_on("ncurses") - - # zoltan and parmetis are linked directly, not just via pumi, so each - # needs its own *_DIR in setup_build_environment. - # +shared: pumi's static libs aren't built with -fPIC and can't link - # into the shared MeshAdaptPUMI.MeshAdapt extension. - # @4.1.0: proteus's own MeshAdaptPUMI source (main) requires the newer - # PCU API (pcu/PCU_C.h) that pumi's older versions predate -- pinned - # explicitly rather than relying on Spack's default latest-version - # preference, since pumi still offers 2.2.9 too (see pumi's own - # package.py for the full explanation). - depends_on("pumi@4.1.0+zoltan+shared", when="+scorec") - # ~fortran: unused here, and zoltan's Fortran interface leaves - # unresolved libgfortran symbols in libzoltan.so on this toolchain. - depends_on("zoltan+parmetis~fortran", when="+scorec") - depends_on("parmetis", when="+scorec") - - # Header-only deps: get_xtensor_include() looks under sys.prefix/include - # rather than a *_DIR var, so these are added to CPATH instead. - depends_on("eigen@3.4") - # 0.27.1 + py-pybind11@3.x fails to compile (see pybind11 pin above). + depends_on("triangle")#shells out but has linked in past + depends_on("tetgen")#shells out + depends_on("gmsh")#shells oput + depends_on("ncurses")#Fenton waves as text gui, generally not used + depends_on("pumi@4.2.1+zoltan+shared", when="+pumi")#<4.2.1 requires patch + depends_on("zoltan+parmetis~fortran", when="+pumi") + depends_on("parmetis", when="+pumi") + depends_on("eigen@3.4")#xtensor dep depends_on("xtensor@0.26.0") - # 0.28.0 is exploratory, added locally to xtensor_python; builtin - # spack-packages only had 0.23.1, which forces pybind11@2.2. depends_on("xtensor-python@0.28.0:") depends_on("xtl") def _mpi_dir(self): - # config/default.py's get_flags('mpi') looks for $MPI_DIR/include/ - # mpi.h -- true for a normal, self-contained MPI prefix (e.g. a - # Spack-built mpich/openmpi), but not for Debian/Ubuntu's system - # mpich package: when Spack's external-package detection picks that - # up (as it does whenever `spack external find` runs on a machine - # with mpich already apt-installed), self.spec["mpi"].prefix is the - # generic /usr, which has no include/mpi.h at all -- Debian's mpich - # package instead nests everything under a per-implementation, - # multiarch-qualified directory (/usr/lib//mpich/include/ - # mpi.h) so it can coexist with an openmpi package's own headers at - # the same prefix. Confirmed directly on this machine: proteus's - # pip-install pathway hit the identical gap and needed the same - # kind of explicit redirect. Prefer the plain prefix/include/mpi.h - # layout when it exists (true for every Spack-built MPI), and only - # fall back to searching for Debian's nested layout otherwise. + # try to find the right mpi.h as it's not always in self.spec["mpi"] mpi_prefix = self.spec["mpi"].prefix if os.path.isfile(os.path.join(mpi_prefix, "include", "mpi.h")): return mpi_prefix @@ -159,17 +67,12 @@ def _mpi_dir(self): for candidate in glob.glob(os.path.join(mpi_prefix, "lib", "*", "openmpi")): if os.path.isfile(os.path.join(candidate, "include", "mpi.h")): return candidate - # No known layout matched -- fall back to the plain prefix so the - # resulting error (if any) still points at a real, inspectable path - # rather than silently returning something clearly wrong. return mpi_prefix def setup_build_environment(self, env): env.set("PROTEUS_SKIP_CHRONO", "1") - if self.spec.satisfies("~scorec"): + if self.spec.satisfies("~pumi"): env.set("PROTEUS_SKIP_PUMI", "1") - - # config/default.py's get_flags() reads a _DIR per dependency. env.set("PETSC_DIR", self.spec["petsc"].prefix) env.set("MPI_DIR", self._mpi_dir()) env.set("HDF5_DIR", self.spec["hdf5"].prefix) @@ -178,11 +81,9 @@ def setup_build_environment(self, env): env.set("SUPERLU_DIR", self.spec["superlu"].prefix) env.set("TRIANGLE_DIR", self.spec["triangle"].prefix) env.set("NCURSES_DIR", self.spec["ncurses"].prefix) - # Needed unconditionally now (config/default.py's get_flags('metis') - # feeds superluWrappers'/csmoothers' link line), not just +scorec. env.set("METIS_DIR", self.spec["metis"].prefix) - if self.spec.satisfies("+scorec"): + if self.spec.satisfies("+pumi"): env.set("SCOREC_DIR", self.spec["pumi"].prefix) env.set("ZOLTAN_DIR", self.spec["zoltan"].prefix) env.set("PARMETIS_DIR", self.spec["parmetis"].prefix) diff --git a/repos/spack_repo/builtin/packages/triangle/package.py b/repos/spack_repo/builtin/packages/triangle/package.py index e76962a1456..7a5d5520037 100644 --- a/repos/spack_repo/builtin/packages/triangle/package.py +++ b/repos/spack_repo/builtin/packages/triangle/package.py @@ -24,51 +24,10 @@ class Triangle(Package): version("1.6", sha256="1766327add038495fa3499e9b7cc642179229750f7201b94f8e1b7bee76f8480") depends_on("libx11", type=("build", "link")) - # Xlib.h itself #includes X11/X.h (protocol constants), which libx11 - # doesn't re-export -- it comes from xproto, already pulled in - # transitively as one of libx11's own build deps, but that isn't enough - # to put its prefix on this package's own include path below; depend on - # it directly so spec["xproto"] is guaranteed available here too - # (confirmed needed via a real build: showme failed with "X11/X.h: No - # such file or directory" once Xlib.h's own location was fixed). depends_on("xproto", type="build") depends_on("gmake", type="build") def install(self, spec, prefix): - # triangle 1.6's own makefile hardcodes CSWITCHES to - # "-O -DLINUX -I/usr/X11R6/include -L/usr/X11R6/lib" unconditionally, - # regardless of the actual build platform, and CSWITCHES must be - # overridden on every platform (not just Darwin) for two independent - # reasons: - # - showme.c #includes X11/Xlib.h directly, and the hardcoded - # -I/usr/X11R6/include is a legacy path that doesn't exist once - # X11 comes from Spack's own libx11 dependency rather than a - # system install (confirmed via a real build: showme failed with - # "X11/Xlib.h: No such file or directory" even with libx11 built - # as a dependency above -- the generic build system used here - # doesn't auto-inject dependency include/lib paths the way - # AutotoolsPackage/CMakePackage do, so they must be wired up - # explicitly here). - # - -DLINUX gates an unconditional `#include ` in - # triangle.c (used to twiddle the legacy x87 FPU's - # precision-control register on old x86 hardware -- irrelevant on - # Apple Silicon, unnecessary on modern x86_64 where doubles use - # SSE by default) via a glibc/Linux-only header that doesn't - # exist on macOS at all; keep it on Linux, drop it on Darwin. - # - triangle.c/showme.c are K&R-style C (old-style function - # definitions, no prototypes in scope at call sites) -- harmless - # under GCC's old implicit-int/implicit-declaration defaults, but - # a sufficiently modern GCC (confirmed reproducing on 15.2.0; - # apparently not triggered on whatever GCC version/platform this - # fix was originally verified against) defaults to a C standard - # that treats a call to an as-yet-undeclared function as - # returning int with no parameters, then reports a hard "too many - # arguments" error once it reaches that function's own - # (parameter-bearing) K&R definition later in the file, rather - # than the warning older defaults gave. PETSc's own Triangle.py - # package hits the identical failure and works around it exactly - # this way -- add -std=gnu17 (old enough to keep implicit - # declarations as a warning, not an error) unconditionally. x11 = spec["libx11"].prefix xproto = spec["xproto"].prefix cswitches = "-O -std=gnu17 -I{0} -I{1} -L{2}".format(x11.include, xproto.include, x11.lib) @@ -80,17 +39,6 @@ def install(self, spec, prefix): install("triangle", prefix.bin) install("showme", prefix.bin) - # This package previously only installed the CLI executables above, - # nothing a dependent could compile/link against -- but consumers - # like proteus link directly against triangle's C API (a single - # object file, triangle.o, built from triangle.c with -DTRILIBRARY - # per the upstream makefile's own "trilibrary" target) rather than - # shelling out to the CLI. Without an installed header+library, - # proteus's own build fails with "fatal error: 'triangle.h' file not - # found" (confirmed via a real `spack install py-proteus` build). - # Build and install both, matching what proteus's config/default.py - # expects: $TRIANGLE_DIR/include/triangle.h and - # $TRIANGLE_DIR/lib/libtri.a. make("CSWITCHES=" + cswitches, "trilibrary") mkdirp(prefix.include) mkdirp(prefix.lib) diff --git a/repos/spack_repo/builtin/packages/zoltan/package.py b/repos/spack_repo/builtin/packages/zoltan/package.py index c7e5cec960f..6fda8e5511d 100644 --- a/repos/spack_repo/builtin/packages/zoltan/package.py +++ b/repos/spack_repo/builtin/packages/zoltan/package.py @@ -120,16 +120,6 @@ def configure_args(self): # Although adding to config_libs _should_ suffice, it does not # Add to ldflags as well config_ldflags.append("-lgfortran") - # A bare -lgfortran only resolves if libgfortran sits on the - # linker's default search path -- true for a system-package- - # manager-provided gcc, but not for a from-source Spack-built - # gcc, whose runtime libs live in its own package prefix. - # Without an explicit -L here this fails outright on macOS - # ("ld: library 'gfortran' not found"; confirmed via a real - # `spack install py-proteus+scorec` build). Ask the Fortran - # compiler itself where its own libgfortran lives rather than - # guessing a layout, so this works regardless of the actual - # gcc package/version Spack picked. gfortran_lib = Executable(self.compiler.fc)( "-print-file-name=libgfortran." + dso_suffix, output=str ).strip() @@ -227,22 +217,6 @@ def solib_install(self): for lib_path in find(self.spec.prefix.lib, "lib*.a"): lib_shared_name = re.sub(r"\.a$", f".{dso_suffix}", lib_path) move(lib_path, lib_shared_name) - # The `--with-ar="$(CXX) -shared $(LDFLAGS) -o"` trick above - # (needed since Zoltan's own build system only ever knows how to - # *archive* into a .a, never link a real shared library) compiles - # a genuine Mach-O dylib, but names the link command's `-o` - # output "libzoltan.a" -- and without an explicit -install_name, - # clang bakes that literal, path-less string into the dylib's - # own LC_ID_DYLIB. Renaming the file on disk just above doesn't - # touch that embedded metadata: any consumer linked against this - # library afterward (pumi's libapf_zoltan.dylib, confirmed via a - # real `spack install py-proteus+scorec` build) captures - # "libzoltan.a" as *its own* reference -- a bare filename with no - # directory, which no rpath entry can resolve, so it fails to - # dlopen ("Library not loaded: libzoltan.a") however correct its - # own rpath list otherwise is. Fix the dylib's self-declared ID - # to a proper @rpath-relative one to match what it was renamed - # to, so downstream linkers record something resolvable instead. if dso_suffix == "dylib": install_name_tool = which("install_name_tool", required=True) install_name_tool( From 0d57bf6015f361fe5a6a5e6e0da0c0e8abcab55f Mon Sep 17 00:00:00 2001 From: Satish Balay Date: Tue, 18 Aug 2026 08:44:58 -0500 Subject: [PATCH 15/28] Update repos/spack_repo/builtin/packages/petsc/package.py --- repos/spack_repo/builtin/packages/petsc/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index 036847988a2..0ea71ddd424 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -547,7 +547,7 @@ def configure_options(self): ("kokkos", "kokkos", False, False), ("kokkos-kernels", "kokkos-kernels", False, False), ("superlu-dist", "superlu_dist", True, True), - ("superlu", "superlu", True, True), + "superlu", ("scotch", "ptscotch", True, True), ( "suite-sparse:umfpack,klu,cholmod,btf,ccolamd,colamd,camd,amd," From 089388d02ad3939f090aa5e64717bf5c24c7b6e1 Mon Sep 17 00:00:00 2001 From: Satish Balay Date: Tue, 18 Aug 2026 08:45:09 -0500 Subject: [PATCH 16/28] Update repos/spack_repo/builtin/packages/petsc/package.py --- repos/spack_repo/builtin/packages/petsc/package.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index 0ea71ddd424..fb94497e4fb 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -363,7 +363,10 @@ class Petsc(Package, CudaPackage, ROCmPackage): depends_on("fftw+mpi", when="+fftw+mpi") depends_on("suite-sparse", when="+suite-sparse") depends_on("superlu", when="+superlu") - conflicts("+superlu+int64", msg="SuperLU has no support for 64-bit integers, use superlu-dist instead") + conflicts( + "+superlu+int64", + msg="SuperLU has no support for 64-bit integers, use superlu-dist instead" + ) depends_on("libx11", when="+X") depends_on("mpfr", when="+mpfr") depends_on("gmp", when="+mpfr") From 4818467817f713873b389d4e629228662ace919d Mon Sep 17 00:00:00 2001 From: Satish Balay Date: Tue, 18 Aug 2026 08:48:28 -0500 Subject: [PATCH 17/28] Apply suggestions from code review Co-authored-by: Satish Balay --- repos/spack_repo/builtin/packages/petsc/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index fb94497e4fb..84b3d5a5127 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -550,7 +550,7 @@ def configure_options(self): ("kokkos", "kokkos", False, False), ("kokkos-kernels", "kokkos-kernels", False, False), ("superlu-dist", "superlu_dist", True, True), - "superlu", + "superlu", ("scotch", "ptscotch", True, True), ( "suite-sparse:umfpack,klu,cholmod,btf,ccolamd,colamd,camd,amd," From 42a06724ce496d230bfa0ca26a37a353cd84f937 Mon Sep 17 00:00:00 2001 From: cekees Date: Tue, 18 Aug 2026 14:00:20 +0000 Subject: [PATCH 18/28] [@spackbot] updating style on behalf of cekees --- .../builtin/packages/petsc/package.py | 6 ++--- .../builtin/packages/py_proteus/package.py | 22 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/repos/spack_repo/builtin/packages/petsc/package.py b/repos/spack_repo/builtin/packages/petsc/package.py index 84b3d5a5127..bf0c0903d78 100644 --- a/repos/spack_repo/builtin/packages/petsc/package.py +++ b/repos/spack_repo/builtin/packages/petsc/package.py @@ -136,9 +136,7 @@ class Petsc(Package, CudaPackage, ROCmPackage): when="+fortran", description="Activates support for superlu-dist (only parallel)", ) - variant( - "superlu", default=False, description="Activates support for superlu (sequential)" - ) + variant("superlu", default=False, description="Activates support for superlu (sequential)") variant("strumpack", default=False, description="Activates support for Strumpack") variant( "scalapack", default=False, when="+fortran", description="Activates support for Scalapack" @@ -365,7 +363,7 @@ class Petsc(Package, CudaPackage, ROCmPackage): depends_on("superlu", when="+superlu") conflicts( "+superlu+int64", - msg="SuperLU has no support for 64-bit integers, use superlu-dist instead" + msg="SuperLU has no support for 64-bit integers, use superlu-dist instead", ) depends_on("libx11", when="+X") depends_on("mpfr", when="+mpfr") diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index ea0a7d51be1..5f90edbf5b5 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -28,30 +28,30 @@ class PyProteus(PythonPackage): depends_on("c", type="build") depends_on("cxx", type="build") - depends_on("fortran", type="build")#remove in future not a direct proteus dep + depends_on("fortran", type="build") # remove in future not a direct proteus dep depends_on("python@3.9:", type=("build", "run")) depends_on("py-setuptools@61:", type="build") depends_on("py-cython@3:", type="build") - depends_on("py-pybind11@2.11:2", type="build") #xtensor@0.27.1 *= overload issue + depends_on("py-pybind11@2.11:2", type="build") # xtensor@0.27.1 *= overload issue depends_on("py-numpy@1.25:", type=("build", "run")) depends_on("py-scipy", type=("build", "run")) depends_on("py-mpi4py", type=("build", "run")) depends_on("py-petsc4py", type=("build", "run")) depends_on("py-h5py+mpi", type=("build", "run")) depends_on("mpi") - depends_on("petsc+mpi+hypre+superlu-dist+superlu+tetgen") #how much to lock in? + depends_on("petsc+mpi+hypre+superlu-dist+superlu+tetgen") # how much to lock in? depends_on("hdf5+mpi+hl") - depends_on("openblas")#should relax to generic blas in future - depends_on("metis")#through several dependencies + depends_on("openblas") # should relax to generic blas in future + depends_on("metis") # through several dependencies depends_on("superlu") - depends_on("triangle")#shells out but has linked in past - depends_on("tetgen")#shells out - depends_on("gmsh")#shells oput - depends_on("ncurses")#Fenton waves as text gui, generally not used - depends_on("pumi@4.2.1+zoltan+shared", when="+pumi")#<4.2.1 requires patch + depends_on("triangle") # shells out but has linked in past + depends_on("tetgen") # shells out + depends_on("gmsh") # shells oput + depends_on("ncurses") # Fenton waves as text gui, generally not used + depends_on("pumi@4.2.1+zoltan+shared", when="+pumi") # <4.2.1 requires patch depends_on("zoltan+parmetis~fortran", when="+pumi") depends_on("parmetis", when="+pumi") - depends_on("eigen@3.4")#xtensor dep + depends_on("eigen@3.4") # xtensor dep depends_on("xtensor@0.26.0") depends_on("xtensor-python@0.28.0:") depends_on("xtl") From 75c3720b003ee06b978521bd03769fb4912e0b5a Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Tue, 18 Aug 2026 14:19:39 -0500 Subject: [PATCH 19/28] pumi: add missing 4.2.1 version tag (same commit as 4.2.0, includes apfMDS.cc int/long fix) --- repos/spack_repo/builtin/packages/pumi/package.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/repos/spack_repo/builtin/packages/pumi/package.py b/repos/spack_repo/builtin/packages/pumi/package.py index 0c95e893f02..0103b72bb9f 100644 --- a/repos/spack_repo/builtin/packages/pumi/package.py +++ b/repos/spack_repo/builtin/packages/pumi/package.py @@ -33,6 +33,9 @@ class Pumi(CMakePackage): # scorec/core develop branch and we prefer not to expose spack users # to the added instability. version("master", submodules=True, branch="master") + version( + "4.2.1", submodules=True, commit="a40922de30f09af63a0251a0d53f95c6cadd9199" + ) # tag 4.2.1 (same commit as 4.2.0 -- retag only, includes the apfMDS.cc int/long fix) version( "4.2.0", submodules=True, commit="a40922de30f09af63a0251a0d53f95c6cadd9199" ) # tag 4.2.0 From e1368f713593870d1b6db6e1517c6b94e5af9fa0 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:35:23 +0200 Subject: [PATCH 20/28] chrono: new package Project Chrono, the multi-physics simulation engine. No Spack package existed for it, which is why proteus's py-proteus recipe sets PROTEUS_SKIP_CHRONO=1 and disables Chrono support outright. Builds the C++ libraries, and with +python the SWIG-generated pychrono module, which Chrono produces from the same tree rather than shipping separately. Optional modules that pull dependencies unrelated to multibody dynamics (cascade, irrlicht, matlab, postprocess, ros, vehicle) are off. Verified by building py-proteus against it and running proteus's suite. With Chrono disabled that pathway reports 508 passed / 6 failed / 2 errors from 514 collected; with this package it reports 517 passed / 0 failed / 1 error from 517 collected. Every chrono-dependent test passes -- AddedMass 2D/3D, four FSI cases, and test_mbd_chrono, whose three tests account for 514 -> 517. The remaining error is unrelated (a dead `import cython` in proteus's own test_bodydynamics.py, since fixed upstream). Four things here are not obvious, and each cost a build to find: - depends_on("c") as well as "cxx". Chrono compiles C sources (chrono_thirdparty/libstl/stlfile.c); without it cmake fails immediately with "[spack cc]: Error: SPACK_CC_* variables not set". - NOT extends("python"). Chrono installs pychrono into share/chrono/python, never into site-packages -- an installed prefix contains only include/ lib/ share/ importer_blender/. extends() promises spack a layout that does not exist, and the module is then simply unimportable. Setting CH_INSTALL_PYTHON does not move it either (tried against 10.0.0). PYTHONPATH is exported for this package and its dependents instead, which is what PETSc's own proteus build does. Dependents must declare chrono a run dependency for that to apply. - CH_DEBUG_POSTFIX forced empty; Chrono otherwise appends "_d" to every library name in a Debug build while consumers link the unsuffixed name. - Python3_INCLUDE_DIR/Python3_LIBRARY set explicitly, because Chrono's find_package(Python3 COMPONENTS Development) has been seen to populate only Python3_EXECUTABLE, after which the SWIG module links without libpython and fails on every CPython symbol. On darwin the module also needs -undefined dynamic_lookup: chrono_python's _core target neither links -lpython nor passes it, which is fine on Linux (resolved at dlopen) but a hard link error for a macOS bundle. Also adds the include ChClassFactory.h relies on transitively. It uses std::enable_if/is_polymorphic/is_abstract but includes only ; libstdc++ and Apple's libc++ still provide it transitively, newer libc++ does not, and Chrono_core then fails with "no member named 'is_polymorphic' in namespace 'std'". Done as a filter_file so it becomes a no-op once upstream adds it. Co-Authored-By: Claude Opus 5 --- .../builtin/packages/chrono/package.py | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 repos/spack_repo/builtin/packages/chrono/package.py diff --git a/repos/spack_repo/builtin/packages/chrono/package.py b/repos/spack_repo/builtin/packages/chrono/package.py new file mode 100644 index 00000000000..5afad3f99e1 --- /dev/null +++ b/repos/spack_repo/builtin/packages/chrono/package.py @@ -0,0 +1,158 @@ +# Copyright Spack Project Developers. See COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import os + +from spack_repo.builtin.build_systems.cmake import CMakePackage + +from spack.package import * + + +class Chrono(CMakePackage): + """Project Chrono is an open-source multi-physics simulation engine for + rigid and flexible multibody dynamics, collision detection, granular + material, fluid-solid interaction and vehicle dynamics. + + The ``+python`` variant additionally builds ``pychrono``, the SWIG-generated + Python module, which Chrono builds from the same tree rather than shipping + as a separate project. + """ + + homepage = "https://projectchrono.org/" + url = "https://github.com/projectchrono/chrono/archive/refs/tags/10.0.0.tar.gz" + git = "https://github.com/projectchrono/chrono.git" + + maintainers("cekees") + + license("BSD-3-Clause") + + version("main", branch="main") + version( + "10.0.0", + sha256="806e5e24a06f26bbd42344dd1f13d75e3214c9eb29901553574b9c87217d8722", + ) + + variant("python", default=False, description="Build the pychrono Python module") + variant("openmp", default=True, description="Enable OpenMP parallelism") + variant("shared", default=True, description="Build shared libraries") + variant("simd", default=False, description="Enable SIMD vectorization") + + # Chrono compiles C as well as C++ (e.g. chrono_thirdparty/libstl/stlfile.c), + # so both must be declared -- omitting "c" fails at cmake time with + # "[spack cc]: Error: SPACK_CC_* variables not set". + depends_on("c", type="build") + depends_on("cxx", type="build") + depends_on("cmake@3.18:", type="build") + + # Chrono's core headers include Eigen directly, so it is a link-time + # dependency of every consumer, not just a build-time one. + depends_on("eigen@3.3.0:") + + # The Python module is generated with SWIG at build time + # (src/chrono_swig/chrono_python/CMakeLists.txt does find_package(SWIG)). + with when("+python"): + # NOT extends("python"): Chrono installs pychrono into + # share/chrono/python, never into site-packages -- verified on an actual + # install, whose prefix contains only include/ lib/ share/ + # importer_blender/. extends() would promise spack a layout that does not + # exist. PYTHONPATH is set for this package and its dependents below, + # which is the same thing PETSc's own proteus build does + # (PYTHONPATH="$PREFIX/share/chrono/python"). + depends_on("python@3.8:", type=("build", "run")) + depends_on("swig@4:", type="build") + + depends_on("llvm-openmp", when="+openmp platform=darwin") + + # ------------------------------------------------------------------ + # ChClassFactory.h uses std::enable_if, std::is_polymorphic and + # std::is_abstract but includes only + # , relying on one of those to pull in + # transitively. libstdc++ and Apple's libc++ still do; + # newer libc++ (e.g. conda-forge's) does not, and Chrono_core then fails + # with 14 errors of the form + # + # ChClassFactory.h:48:53: error: no member named 'is_polymorphic' in + # namespace 'std' + # + # Belongs upstream in projectchrono/chrono; done here as a filter_file + # rather than a patch so it stays a no-op once upstream adds the include. + # ------------------------------------------------------------------ + @run_before("cmake") + def add_missing_type_traits_include(self): + header = join_path(self.stage.source_path, "src", "chrono", "core", "ChClassFactory.h") + if not os.path.exists(header): + return + with open(header) as f: + if "#include " in f.read(): + return + filter_file( + r"^#include $", + "#include \n#include ", + header, + ) + + def cmake_args(self): + args = [ + self.define_from_variant("BUILD_SHARED_LIBS", "shared"), + self.define("BUILD_TESTING", False), + self.define("BUILD_DEMOS", False), + # Optional modules, each pulling dependencies Chrono does not need + # for multibody dynamics. Option names carry the CH_ prefix as of + # 10.0.0 (confirmed against the tag's own CMakeLists.txt). + self.define("CH_ENABLE_MODULE_CASCADE", False), + self.define("CH_ENABLE_MODULE_IRRLICHT", False), + self.define("CH_ENABLE_MODULE_MATLAB", False), + self.define("CH_ENABLE_MODULE_POSTPROCESS", False), + self.define("CH_ENABLE_MODULE_ROS", False), + self.define("CH_ENABLE_MODULE_VEHICLE", False), + self.define_from_variant("CH_ENABLE_MODULE_PYTHON", "python"), + self.define_from_variant("CH_ENABLE_OPENMP", "openmp"), + self.define_from_variant("CH_USE_SIMD", "simd"), + # Chrono appends CH_DEBUG_POSTFIX ("_d") to every library name in a + # Debug build, but consumers link the unsuffixed name. Force it + # empty so a Debug build keeps -g without renaming the libraries. + self.define("CH_DEBUG_POSTFIX", ""), + ] + + if self.spec.satisfies("+python"): + # Chrono's find_package(Python3 ... COMPONENTS Development) has been + # observed to populate only Python3_EXECUTABLE, after which the SWIG + # module links without libpython and fails on every CPython symbol. + # Point CMake at the library and headers directly. + python = self.spec["python"] + args += [ + self.define("Python3_EXECUTABLE", python.command.path), + self.define("Python3_INCLUDE_DIR", python.headers.directories[0]), + self.define("Python3_LIBRARY", python.libs[0]), + ] + if self.spec.satisfies("platform=darwin"): + # chrono_python's _core module target neither links -lpython nor + # passes -undefined dynamic_lookup. Unresolved symbols in a + # shared object are fine on Linux and resolve at dlopen time, + # but are a hard link error for a macOS bundle. + args.append( + self.define("CMAKE_MODULE_LINKER_FLAGS", "-undefined dynamic_lookup") + ) + + return args + + # pychrono lives in share/chrono/python, so nothing finds it by default. + # Setting CH_INSTALL_PYTHON does not move it (tried against 10.0.0: the + # module still installed to share/chrono/python), so expose it explicitly -- + # for this package's own run environment and for anything depending on it. + @property + def _pychrono_dir(self): + return join_path(self.prefix, "share", "chrono", "python") + + def setup_run_environment(self, env): + if self.spec.satisfies("+python"): + env.prepend_path("PYTHONPATH", self._pychrono_dir) + + def setup_dependent_build_environment(self, env, dependent_spec): + if self.spec.satisfies("+python"): + env.prepend_path("PYTHONPATH", self._pychrono_dir) + + def setup_dependent_run_environment(self, env, dependent_spec): + if self.spec.satisfies("+python"): + env.prepend_path("PYTHONPATH", self._pychrono_dir) From e3e8626f80689a040b2b619a8c5dd1e370458b6c Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:17:11 +0200 Subject: [PATCH 21/28] py-proteus: build against chrono instead of disabling it The recipe set PROTEUS_SKIP_CHRONO=1 with the comment "Chrono (pychrono) currently has no upstream Spack package and stays disabled". There is one now (merged into this branch), so proteus can be built with its Chrono coupling. Three changes, each needed: - depends_on("chrono+python") with run type. The run type is not incidental: pychrono installs into share/chrono/python rather than site-packages, so chrono exports PYTHONPATH via setup_dependent_run_environment, and spack only applies that for run dependencies. Omit it and pychrono is unimportable at test time even though everything builds. - CHRONO_DIR in the build environment. proteus/config/default.py's get_flags("chrono") reads $CHRONO_DIR/{include,lib} and pulls CHRONO_CXX_FLAGS out of lib/cmake/Chrono/ChronoConfig.cmake; dropping the skip flag alone is not sufficient. - PROTEUS_SKIP_CHRONO removed. Measured on osx-arm64, proteus at d6341f48, same command either way: without chrono 508 passed / 6 failed / 2 errors / 514 collected with chrono 532 passed / 0 failed / 0 errors / 532 collected The six failures were AddedMass 2D/3D and four FSI cases, all needing proteus.mbd.CouplingFSI; one error was test_mbd_chrono, whose three tests account for 514 -> 517. (The second error was a dead `import cython` in proteus's own test_bodydynamics.py, fixed upstream since; its 15 tests are the 517 -> 532.) Co-Authored-By: Claude Opus 5 --- .../spack_repo/builtin/packages/py_proteus/package.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 5f90edbf5b5..fd46fe834e6 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -23,7 +23,6 @@ class PyProteus(PythonPackage): version("main", branch="main") - # Chrono (pychrono) currently has no upstream Spack package and stays disabled. variant("pumi", default=False, description="Enable PUMI mesh adaptation support") depends_on("c", type="build") @@ -48,6 +47,10 @@ class PyProteus(PythonPackage): depends_on("tetgen") # shells out depends_on("gmsh") # shells oput depends_on("ncurses") # Fenton waves as text gui, generally not used + # run type matters: chrono exports PYTHONPATH for share/chrono/python + # (pychrono is not installed into site-packages) via + # setup_dependent_run_environment, which spack only applies to run deps. + depends_on("chrono+python", type=("build", "link", "run")) depends_on("pumi@4.2.1+zoltan+shared", when="+pumi") # <4.2.1 requires patch depends_on("zoltan+parmetis~fortran", when="+pumi") depends_on("parmetis", when="+pumi") @@ -70,9 +73,12 @@ def _mpi_dir(self): return mpi_prefix def setup_build_environment(self, env): - env.set("PROTEUS_SKIP_CHRONO", "1") if self.spec.satisfies("~pumi"): env.set("PROTEUS_SKIP_PUMI", "1") + # proteus/config/default.py get_flags("chrono") reads $CHRONO_DIR/ + # {include,lib} and pulls CHRONO_CXX_FLAGS out of + # lib/cmake/Chrono/ChronoConfig.cmake. + env.set("CHRONO_DIR", self.spec["chrono"].prefix) env.set("PETSC_DIR", self.spec["petsc"].prefix) env.set("MPI_DIR", self._mpi_dir()) env.set("HDF5_DIR", self.spec["hdf5"].prefix) From 0cf11cba78a275063171ba984d8674edcba14ba0 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:12:30 +0200 Subject: [PATCH 22/28] chrono: satisfy ruff-format Three calls were wrapped across lines that fit inside spack's 99-column limit, which ruff-format rejects. Formatting only, no semantic change; applied with `spack style --fix`. The prechecks/style job failed on this in 13s. The F403/F405 star-import warnings that also appear locally are not the cause -- they fire on every spack package using `from spack.package import *`, including py_proteus's own pre-existing depends_on lines. Co-Authored-By: Claude Opus 5 --- .../spack_repo/builtin/packages/chrono/package.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/repos/spack_repo/builtin/packages/chrono/package.py b/repos/spack_repo/builtin/packages/chrono/package.py index 5afad3f99e1..6a515f63052 100644 --- a/repos/spack_repo/builtin/packages/chrono/package.py +++ b/repos/spack_repo/builtin/packages/chrono/package.py @@ -28,10 +28,7 @@ class Chrono(CMakePackage): license("BSD-3-Clause") version("main", branch="main") - version( - "10.0.0", - sha256="806e5e24a06f26bbd42344dd1f13d75e3214c9eb29901553574b9c87217d8722", - ) + version("10.0.0", sha256="806e5e24a06f26bbd42344dd1f13d75e3214c9eb29901553574b9c87217d8722") variant("python", default=False, description="Build the pychrono Python module") variant("openmp", default=True, description="Enable OpenMP parallelism") @@ -86,11 +83,7 @@ def add_missing_type_traits_include(self): with open(header) as f: if "#include " in f.read(): return - filter_file( - r"^#include $", - "#include \n#include ", - header, - ) + filter_file(r"^#include $", "#include \n#include ", header) def cmake_args(self): args = [ @@ -131,9 +124,7 @@ def cmake_args(self): # passes -undefined dynamic_lookup. Unresolved symbols in a # shared object are fine on Linux and resolve at dlopen time, # but are a hard link error for a macOS bundle. - args.append( - self.define("CMAKE_MODULE_LINKER_FLAGS", "-undefined dynamic_lookup") - ) + args.append(self.define("CMAKE_MODULE_LINKER_FLAGS", "-undefined dynamic_lookup")) return args From 757663ae110359085e902829a31a0ce5b3f0ebd2 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 28 Aug 2026 21:04:35 +0200 Subject: [PATCH 23/28] py-proteus: depend on gmsh~fltk~med proteus shells out to the gmsh executable as a mesh generator. It needs neither gmsh's FLTK GUI nor MED file support, but both are gmsh defaults and both pull in dependencies that fail to build on current toolchains: +fltk -> mesa@25.0.5, whose vendored src/c11/threads.h defines once_flag and call_once and so conflicts with glibc 2.43's own ("conflicting types for 'once_flag'; have 'pthread_once_t'") +med -> med@5.0.0, which passes an incompatible pointer as argument 5 of H5Literate2 and is rejected by gcc >= 14, where -Wincompatible-pointer-types is an error Excluding both drops them from the DAG entirely and fixes the build on Ubuntu 26.04 / gcc 15.2 / glibc 2.43, where py-proteus+pumi previously could not be built at all. It also removes mesa, one of the heavier items in the tree, from every other platform's build. Verified on Ubuntu 26.04 LTS, gcc 15.2.0, glibc 2.43: spack install py-proteus+pumi completes, the gmsh executable is present and functional, and the proteus test suite reports 532 passed, 0 failed. Co-Authored-By: Claude Opus 5 --- .../spack_repo/builtin/packages/py_proteus/package.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index fd46fe834e6..35bdf59e413 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -45,7 +45,16 @@ class PyProteus(PythonPackage): depends_on("superlu") depends_on("triangle") # shells out but has linked in past depends_on("tetgen") # shells out - depends_on("gmsh") # shells oput + # proteus shells out to the gmsh executable as a mesh generator, so it needs + # neither the FLTK GUI nor MED file support. Both are gmsh defaults, and both + # pull in heavy dependencies that fail to build on current toolchains: +fltk + # brings mesa, whose vendored src/c11/threads.h redefines once_flag and + # call_once and so conflicts with glibc 2.43's own ; +med brings + # med, which passes an incompatible pointer to H5Literate2 and is rejected by + # gcc >= 14, where -Wincompatible-pointer-types is an error. Excluding them + # fixes the build on Ubuntu 26.04 / gcc 15.2 and drops a large amount of + # build time everywhere else. + depends_on("gmsh~fltk~med") # shells out to the gmsh executable depends_on("ncurses") # Fenton waves as text gui, generally not used # run type matters: chrono exports PYTHONPATH for share/chrono/python # (pychrono is not installed into site-packages) via From 8c181976482137d2481a7c279d40c23523a67b50 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 28 Aug 2026 22:33:57 +0200 Subject: [PATCH 24/28] openblas: build only the library targets, not the test suite TEMPORARY, pending upstream: this carries a fix for a package we do not own, so that py-proteus can be built on macOS x86_64 at all. Drop it once spack/spack-packages takes the change. build() ran `make` with no target, which is OpenBLAS's default `all`: all :: libs netlib $(RELA) tests shared so the test suite was built and executed by an ordinary `spack install openblas`. That defeats the recipe's own check_build/check_install hooks, both guarded by @on_package_attributes(run_tests=True), which are the intended way to opt into testing -- `--test` added a second test run rather than gating the first. It also makes any test failure a hard build failure. On macOS x86_64 (TARGET=SKYLAKEX NO_AVX512=1 DYNAMIC_ARCH=1, apple-clang 21 + gfortran 16.2): TEST 1/125 min:smin_negative make[1]: *** [Makefile:89: run_test] Bus error: 10 make: *** [Makefile:178: tests] Error 2 The library itself compiles cleanly; only its test binary crashes. Building `libs netlib shared` is `all` minus `tests` (and minus $(RELA), empty unless re-lapack is enabled). Verified on the affected host: openblas installs (10m53s), the full py-proteus+pumi tree then builds with no further failures (30m33s), and the proteus suite reports 532 passed, 0 failed. Co-Authored-By: Claude Opus 5 --- .../spack_repo/builtin/packages/openblas/package.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/openblas/package.py b/repos/spack_repo/builtin/packages/openblas/package.py index 36a62ad3245..3b661690516 100644 --- a/repos/spack_repo/builtin/packages/openblas/package.py +++ b/repos/spack_repo/builtin/packages/openblas/package.py @@ -647,7 +647,18 @@ def build(self, pkg: MakefilePackage, spec: Spec, prefix: Prefix) -> None: with working_dir(self.build_directory): # Due to the verbosity of the command line and number of object # files created, we suppress makefile command echoing via `-s`. - make("-s", *self.make_defs) + # Build only the library targets. A bare "make" is OpenBLAS's default + # "all", which is "libs netlib $(RELA) tests shared" -- so the test + # suite is built AND RUN during an ordinary install. That defeats the + # check_build/check_install hooks below, which are guarded by + # @on_package_attributes(run_tests=True) and are the intended opt-in, + # and it turns any test failure into a hard build failure. On macOS + # x86_64 (TARGET=SKYLAKEX NO_AVX512=1 DYNAMIC_ARCH=1) "TEST 1/125 + # min:smin_negative" dies with "Bus error: 10" and blocks the whole + # platform, though the library itself builds cleanly. + # TEMPORARY: carried on this branch pending an upstream fix; drop it + # once spack/spack-packages takes the change. + make("-s", "libs", "netlib", "shared", *self.make_defs) def edit(self, pkg, spec, prefix): # https://github.com/spack/spack-packages/pull/5883#issuecomment-5189054355 From b9c72ea695b2ac9f78cdd91df59ab3c81b75dd02 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Fri, 28 Aug 2026 23:09:04 +0200 Subject: [PATCH 25/28] openblas: build the library targets sequentially, not as one make Fixes a regression in the previous commit. Passing "libs netlib shared" as three goals to a single make lets them run concurrently, which races: ar: ../libopenblas-r0.3.34.a: error reading zlatm5.o: file truncated make[1]: *** [../Makefile.tail:95: commonlibs] Error 1 on linux-alderlake/gcc 15.2. This is exactly the hazard build()'s docstring names -- "Override 'make all' with sequential builds due to race conditions" -- and the reason the method exists at all. macOS happened to survive it, which is why the first version looked fine. Invoking each target in its own make preserves the ordering while still excluding "tests" from the build, which was the point of the change. Verified on linux-alderlake/gcc 15.2, where the three-goal form failed: openblas installs cleanly (6m35s, exit 0). Co-Authored-By: Claude Opus 5 --- .../builtin/packages/openblas/package.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/repos/spack_repo/builtin/packages/openblas/package.py b/repos/spack_repo/builtin/packages/openblas/package.py index 3b661690516..9ad1693fdd6 100644 --- a/repos/spack_repo/builtin/packages/openblas/package.py +++ b/repos/spack_repo/builtin/packages/openblas/package.py @@ -647,18 +647,26 @@ def build(self, pkg: MakefilePackage, spec: Spec, prefix: Prefix) -> None: with working_dir(self.build_directory): # Due to the verbosity of the command line and number of object # files created, we suppress makefile command echoing via `-s`. - # Build only the library targets. A bare "make" is OpenBLAS's default - # "all", which is "libs netlib $(RELA) tests shared" -- so the test - # suite is built AND RUN during an ordinary install. That defeats the - # check_build/check_install hooks below, which are guarded by + # Build the library targets only, ONE AT A TIME. A bare "make" is + # OpenBLAS's default "all" -- "libs netlib $(RELA) tests shared" -- so + # the test suite is built AND RUN by an ordinary install. That defeats + # the check_build/check_install hooks below, which are guarded by # @on_package_attributes(run_tests=True) and are the intended opt-in, - # and it turns any test failure into a hard build failure. On macOS + # and it turns any test failure into a hard build failure: on macOS # x86_64 (TARGET=SKYLAKEX NO_AVX512=1 DYNAMIC_ARCH=1) "TEST 1/125 - # min:smin_negative" dies with "Bus error: 10" and blocks the whole - # platform, though the library itself builds cleanly. + # min:smin_negative" dies with "Bus error: 10" though the library + # itself builds cleanly. + # + # The invocations must stay SEPARATE. Passing "libs netlib shared" as + # three goals to one make lets them run concurrently, which races: + # "ar: libopenblas-r0.3.34.a: error reading zlatm5.o: file truncated" + # on linux-alderlake/gcc. That is the race this method's docstring is + # about, and the reason it overrides 'make all' in the first place. + # # TEMPORARY: carried on this branch pending an upstream fix; drop it # once spack/spack-packages takes the change. - make("-s", "libs", "netlib", "shared", *self.make_defs) + for target in ("libs", "netlib", "shared"): + make("-s", target, *self.make_defs) def edit(self, pkg, spec, prefix): # https://github.com/spack/spack-packages/pull/5883#issuecomment-5189054355 From fbde1c1b4094b7136de46421392ef6c278c21b0b Mon Sep 17 00:00:00 2001 From: Chris Kees <613654+cekees@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:16:46 +0200 Subject: [PATCH 26/28] Apply suggestion from @balay Co-authored-by: Satish Balay --- repos/spack_repo/builtin/packages/py_proteus/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 35bdf59e413..000500cef53 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -60,7 +60,7 @@ class PyProteus(PythonPackage): # (pychrono is not installed into site-packages) via # setup_dependent_run_environment, which spack only applies to run deps. depends_on("chrono+python", type=("build", "link", "run")) - depends_on("pumi@4.2.1+zoltan+shared", when="+pumi") # <4.2.1 requires patch + depends_on("pumi@4.2.1:+zoltan+shared", when="+pumi") # <4.2.1 requires patch depends_on("zoltan+parmetis~fortran", when="+pumi") depends_on("parmetis", when="+pumi") depends_on("eigen@3.4") # xtensor dep From 525d1f7656117380397660f277783c37e16b89b6 Mon Sep 17 00:00:00 2001 From: cekees <613654+cekees@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:23:53 +0200 Subject: [PATCH 27/28] clean up pumi, chrono, and triangle dependencies - make pumi package.py diff match the upstream PR - properly implement pumi and chron variants - remove triangle library build, which proteus doesn't need --- .../builtin/packages/pumi/package.py | 3 -- .../builtin/packages/py_proteus/package.py | 30 +++++++------------ .../builtin/packages/triangle/package.py | 8 ----- 3 files changed, 11 insertions(+), 30 deletions(-) diff --git a/repos/spack_repo/builtin/packages/pumi/package.py b/repos/spack_repo/builtin/packages/pumi/package.py index 0103b72bb9f..a8ddf81af89 100644 --- a/repos/spack_repo/builtin/packages/pumi/package.py +++ b/repos/spack_repo/builtin/packages/pumi/package.py @@ -36,9 +36,6 @@ class Pumi(CMakePackage): version( "4.2.1", submodules=True, commit="a40922de30f09af63a0251a0d53f95c6cadd9199" ) # tag 4.2.1 (same commit as 4.2.0 -- retag only, includes the apfMDS.cc int/long fix) - version( - "4.2.0", submodules=True, commit="a40922de30f09af63a0251a0d53f95c6cadd9199" - ) # tag 4.2.0 version( "4.1.0", submodules=True, commit="a8e3aef58bfe86790782c4ae5e5c1bb5f232ff30" ) # tag 4.1.0 diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index 000500cef53..b91c3a47879 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -23,7 +23,8 @@ class PyProteus(PythonPackage): version("main", branch="main") - variant("pumi", default=False, description="Enable PUMI mesh adaptation support") + variant("pumi", default=True, description="Enable PUMI mesh adaptation support") + variant("chrono", default=True, description="Enable Chrono modeling support") depends_on("c", type="build") depends_on("cxx", type="build") @@ -38,31 +39,22 @@ class PyProteus(PythonPackage): depends_on("py-petsc4py", type=("build", "run")) depends_on("py-h5py+mpi", type=("build", "run")) depends_on("mpi") - depends_on("petsc+mpi+hypre+superlu-dist+superlu+tetgen") # how much to lock in? + depends_on("petsc+mpi+hypre+superlu-dist+superlu") depends_on("hdf5+mpi+hl") depends_on("openblas") # should relax to generic blas in future depends_on("metis") # through several dependencies depends_on("superlu") depends_on("triangle") # shells out but has linked in past depends_on("tetgen") # shells out - # proteus shells out to the gmsh executable as a mesh generator, so it needs - # neither the FLTK GUI nor MED file support. Both are gmsh defaults, and both - # pull in heavy dependencies that fail to build on current toolchains: +fltk - # brings mesa, whose vendored src/c11/threads.h redefines once_flag and - # call_once and so conflicts with glibc 2.43's own ; +med brings - # med, which passes an incompatible pointer to H5Literate2 and is rejected by - # gcc >= 14, where -Wincompatible-pointer-types is an error. Excluding them - # fixes the build on Ubuntu 26.04 / gcc 15.2 and drops a large amount of - # build time everywhere else. - depends_on("gmsh~fltk~med") # shells out to the gmsh executable + depends_on("gmsh~fltk~med") # shells out to the gmsh, no fltk/med needed depends_on("ncurses") # Fenton waves as text gui, generally not used # run type matters: chrono exports PYTHONPATH for share/chrono/python # (pychrono is not installed into site-packages) via # setup_dependent_run_environment, which spack only applies to run deps. - depends_on("chrono+python", type=("build", "link", "run")) + depends_on("chrono+python", when="+chrono", type=("build", "link", "run")) depends_on("pumi@4.2.1:+zoltan+shared", when="+pumi") # <4.2.1 requires patch depends_on("zoltan+parmetis~fortran", when="+pumi") - depends_on("parmetis", when="+pumi") + depends_on("parmetis") depends_on("eigen@3.4") # xtensor dep depends_on("xtensor@0.26.0") depends_on("xtensor-python@0.28.0:") @@ -84,17 +76,14 @@ def _mpi_dir(self): def setup_build_environment(self, env): if self.spec.satisfies("~pumi"): env.set("PROTEUS_SKIP_PUMI", "1") - # proteus/config/default.py get_flags("chrono") reads $CHRONO_DIR/ - # {include,lib} and pulls CHRONO_CXX_FLAGS out of - # lib/cmake/Chrono/ChronoConfig.cmake. - env.set("CHRONO_DIR", self.spec["chrono"].prefix) + if self.spec.satisfies("~chrono"): + env.set("PROTEUS_SKIP_CHRONO", "1") env.set("PETSC_DIR", self.spec["petsc"].prefix) env.set("MPI_DIR", self._mpi_dir()) env.set("HDF5_DIR", self.spec["hdf5"].prefix) env.set("BLAS_DIR", self.spec["openblas"].prefix) env.set("LAPACK_DIR", self.spec["openblas"].prefix) env.set("SUPERLU_DIR", self.spec["superlu"].prefix) - env.set("TRIANGLE_DIR", self.spec["triangle"].prefix) env.set("NCURSES_DIR", self.spec["ncurses"].prefix) env.set("METIS_DIR", self.spec["metis"].prefix) @@ -103,5 +92,8 @@ def setup_build_environment(self, env): env.set("ZOLTAN_DIR", self.spec["zoltan"].prefix) env.set("PARMETIS_DIR", self.spec["parmetis"].prefix) + if self.spec.satisfies("+chrono"): + env.set("CHRONO_DIR", self.spec["chrono"].prefix) + for dep in ("eigen", "xtensor", "xtensor-python", "xtl"): env.prepend_path("CPATH", self.spec[dep].prefix.include) diff --git a/repos/spack_repo/builtin/packages/triangle/package.py b/repos/spack_repo/builtin/packages/triangle/package.py index 7a5d5520037..261d4899bcc 100644 --- a/repos/spack_repo/builtin/packages/triangle/package.py +++ b/repos/spack_repo/builtin/packages/triangle/package.py @@ -38,11 +38,3 @@ def install(self, spec, prefix): install("triangle", prefix.bin) install("showme", prefix.bin) - - make("CSWITCHES=" + cswitches, "trilibrary") - mkdirp(prefix.include) - mkdirp(prefix.lib) - install("triangle.h", prefix.include) - ar = which("ar", required=True) - ar("rcs", "libtri.a", "triangle.o") - install("libtri.a", prefix.lib) From c0b936a99952fefd52af360e7653723b8aa76ac0 Mon Sep 17 00:00:00 2001 From: cekees Date: Sat, 29 Aug 2026 14:30:12 +0000 Subject: [PATCH 28/28] [@spackbot] updating style on behalf of cekees --- repos/spack_repo/builtin/packages/py_proteus/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/spack_repo/builtin/packages/py_proteus/package.py b/repos/spack_repo/builtin/packages/py_proteus/package.py index b91c3a47879..5ce950bd118 100644 --- a/repos/spack_repo/builtin/packages/py_proteus/package.py +++ b/repos/spack_repo/builtin/packages/py_proteus/package.py @@ -94,6 +94,6 @@ def setup_build_environment(self, env): if self.spec.satisfies("+chrono"): env.set("CHRONO_DIR", self.spec["chrono"].prefix) - + for dep in ("eigen", "xtensor", "xtensor-python", "xtl"): env.prepend_path("CPATH", self.spec[dep].prefix.include)