Build cache coherence across PETSc version switches - #120
Conversation
Previously ./uw petsc switch rewrote PETSC_ARCH values in pixi.toml
via sed, which (1) made every switch a tracked file change, (2)
forced contributors on different PETSc versions to carry diverging
pixi.toml in their working tree, and (3) did not reliably propagate
to pixi's activated environment because pixi caches resolved state.
Replace the five hardcoded PETSC_ARCH = "petsc-*-uw-*" entries (in
features amr [osx-arm64 and linux-64], amr-mpich, amr-openmpi, and
amr-debug) with per-feature activation scripts that source the new
petsc-custom/activate-petsc-arch.sh. The script reads the active
version from petsc-custom/.petsc-version and combines it with the
active $PIXI_ENVIRONMENT_NAME (and uname on Darwin/Linux for the
plain amr env) to construct PETSC_ARCH = petsc-{ver}-uw-{mpi}{suffix}.
Non-AMR environments match no case and leave PETSC_ARCH unset, so
the script is safe as a no-op for default/runtime/dev/mpich/openmpi
environments.
Switching PETSc versions now only updates .petsc-version (one
untracked, gitignored file); pixi.toml is not modified.
Underworld development team with AI support from Claude Code
./uw build has target-change detection (build/.petsc_target) that cleans build/lib.* when petsc4py.get_config() reports a new arch. After ./uw petsc switch, though, the still-installed petsc4py is linked to the OLD arch and continues to report it, so the check does not fire and pip/Cython silently reuse stale wheels against the new PETSc. Force the full rebuild path at switch time: remove build/lib.*, build/temp.*, build/bdist.*, the .petsc_target marker, purge pip's wheel cache, and uninstall petsc4py (AMR envs only). The next ./uw build then rebuilds petsc4py against the new PETSc and recompiles every Cython extension. Underworld development team with AI support from Claude Code
There was a problem hiding this comment.
Pull request overview
This PR aims to make PETSc version switching non-invasive to tracked configuration by keeping pixi.toml static (no more PETSC_ARCH rewrites) and ensuring PETSc switches don’t leave behind stale build artifacts/wheels that cause underworld3/petsc4py to link against the wrong PETSc.
Changes:
- Add a pixi activation script to dynamically export
PETSC_ARCHfor AMR environments based onpetsc-custom/.petsc-versionand the active pixi environment. - Remove hardcoded
PETSC_ARCHentries frompixi.tomlAMR features and wire in the activation script per feature. - On
./uw petsc switch, proactively remove build artifacts/markers and uninstallpetsc4py(AMR envs) to force a clean rebuild on the next./uw build.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
uw |
Adds cleanup steps on petsc switch to prevent stale build reuse after PETSc changes. |
pixi.toml |
Removes hardcoded PETSC_ARCH values and attaches an activation script for AMR features. |
petsc-custom/activate-petsc-arch.sh |
New activation script that derives and exports PETSC_ARCH at environment activation time. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if [ -n "$_uw_mpi" ]; then | ||
| _uw_ver="4" | ||
| _uw_ver_file="${PIXI_PROJECT_ROOT:-.}/petsc-custom/.petsc-version" | ||
| [ -f "$_uw_ver_file" ] && _uw_ver=$(cat "$_uw_ver_file" 2>/dev/null || echo "4") | ||
|
|
||
| export PETSC_ARCH="petsc-${_uw_ver}-uw-${_uw_mpi}${_uw_suffix}" |
There was a problem hiding this comment.
activate-petsc-arch.sh defaults _uw_ver to 4 and reads .petsc-version without trimming whitespace. Elsewhere (uw and petsc-custom/build-petsc.sh) the version defaults to 324 and .petsc-version is expected to contain the short numeric form (e.g. 325). On a fresh checkout where .petsc-version is missing (or contains whitespace), this can export a legacy/invalid PETSC_ARCH and break downstream builds (notably pip install of petsc4py which relies on PETSC_ARCH). Align the default/version-parsing logic with petsc_version_short() (default 324, strip whitespace, and ideally fall back to tag detection when the file is absent).
| [feature.amr-debug.activation] | ||
| scripts = ["petsc-custom/activate-petsc-arch.sh"] | ||
|
|
||
| [feature.amr-debug.activation.env] | ||
| PETSC_DIR = "$PIXI_PROJECT_ROOT/petsc-custom/petsc" |
There was a problem hiding this comment.
The AMR debug setup comment block still hardcodes petsc-4-uw-openmpi-debug, but PETSC_ARCH is now being set via the activation script (and will be version-derived from petsc-custom/.petsc-version). This documentation is now misleading for anyone following the debug setup steps; update it to reflect the new arch naming scheme and clarify whether debug builds should be legacy (petsc-4-...-debug) or versioned (petsc-<ver>-...-debug).
| $PIXI run -e "$(get_env)" pip cache purge 2>/dev/null || true | ||
| if is_amr_env "$(get_env)"; then | ||
| echo "Uninstalling old petsc4py (linked to previous PETSc build)..." | ||
| $PIXI run -e "$(get_env)" pip uninstall -y petsc4py 2>/dev/null || true | ||
| fi |
There was a problem hiding this comment.
pip cache purge clears the entire pip cache (not just PETSc/petsc4py artifacts). If the goal is specifically to prevent reuse of cached petsc4py/UW wheels across PETSc switches, consider a more targeted approach (e.g. removing only the relevant cached wheels, or ensuring the subsequent installs use --no-cache-dir/--force-reinstall) to avoid unnecessarily slowing unrelated installs.
| esac ;; | ||
| amr-mpich|amr-mpich-dev) _uw_mpi="mpich" ;; | ||
| amr-openmpi|amr-openmpi-dev) _uw_mpi="openmpi" ;; | ||
| amr-debug) _uw_mpi="openmpi"; _uw_suffix="-debug" ;; |
There was a problem hiding this comment.
In amr-debug the script hardcodes _uw_mpi="openmpi", but on linux-64 the amr feature dependencies use MPICH (see pixi.toml target deps). That makes PETSC_ARCH inconsistent with the MPI actually present in the environment and can lead to PETSc/petsc4py being built/loaded against the wrong MPI. Consider selecting MPI for amr-debug the same way as amr (platform-default), or provide explicit amr-debug-mpich / amr-debug-openmpi envs/features to avoid cross-platform mismatches.
| amr-debug) _uw_mpi="openmpi"; _uw_suffix="-debug" ;; | |
| amr-debug) | |
| case "$(uname -s)" in | |
| Darwin) _uw_mpi="openmpi" ;; | |
| *) _uw_mpi="mpich" ;; | |
| esac | |
| _uw_suffix="-debug" ;; |
…ig()
When pixi activation exports PETSC_DIR and PETSC_ARCH for a custom
build, setup.py was ignoring them and reading petsc4py.get_config()
instead. That reports the arch petsc4py was BUILT against — so
immediately after a PETSc version switch, setup.py would silently
link the rebuild against the previous PETSc, not the new one.
Re-order the PETSc discovery into an explicit priority chain:
1. PETSC_DIR / PETSC_ARCH from the environment (pixi activation,
shell export, HPC module load)
2. petsc4py.get_config() — authoritative match for installed petsc4py
3. conda `petsc` pip package
4. CONDA_PREFIX/lib/.../site-packages/petsc fallback
The previous code only consulted env vars as a last-resort branch of
the conda fallback, which was exactly backwards — env vars are the
most specific signal and should win.
Underworld development team with AI support from Claude Code
- activate-petsc-arch.sh: platform-aware MPI for amr-debug (was hardcoded openmpi, wrong on linux-64 where feature.amr pulls mpich). Version discovery now matches ./uw petsc_version_short: strip whitespace from .petsc-version, default to 324 (not "4", which would produce a legacy arch name that no current build tree uses). - pixi.toml: update the amr-debug setup comment to refer to "$PETSC_ARCH" (now exported by the activation script) instead of the stale hardcoded "petsc-4-uw-openmpi-debug" string. - uw: drop the global `pip cache purge` on ./uw petsc switch — it clears every project's pip wheel cache, which is overkill. The two installs that matter already avoid the cache: run_build passes --no-cache-dir to `pip install .`, and petsc4py's local-path install now does the same. Underworld development team with AI support from Claude Code
Why
Issue #96 (BdIntegral parallel hang) was fixed by a PETSc version bump that ships the upstream ownership fix. That merged in #115, but after switching PETSc the build system was leaving stale compiled code in place: users who pulled the fix and did
./uw petsc switch+./uw buildstill loaded extensions linked against the previous PETSc. The #96 fix wasn't actually reaching their installs, and code like jcgraciosa's that depended on it stayed broken.Three places were silently assuming PETSc never changes during the life of an install. Fixing only one or two still leaves a stale-load path open; this PR fixes all three so the chain from
./uw petsc switch→./uw build→import underworld3is consistent.The three fixes
1.
setup.py— honourPETSC_DIR/PETSC_ARCHenv vars firstsetup.py::configure()was tryingimport petsc(the conda pip package) first, which returns the conda PETSc path even when$PETSC_DIR/$PETSC_ARCHpoint at a custom build — so the custom build was silently ignored and underworld3 compiled against the wronglibpetsc. It also only consulted env vars as a corner case inside the conda fallback.Explicit priority chain now:
$PETSC_DIR/$PETSC_ARCHfrom the environment (pixi activation, shell, HPC module)petsc4py.get_config()— authoritative match for the installedpetsc4pypetscpip packageCONDA_PREFIX/lib/.../site-packages/petscfallbackEnv vars are the most specific signal and should win.
2.
./uw petsc switch— nuke stale wheels andpetsc4py./uw buildalready has target-change detection viabuild/.petsc_target, but it readspetsc4py.get_config()to identify the "current" target. Right after a switch,petsc4pyis still the old one and reports the old arch, so the check doesn't fire, pip's wheel cache (version0.0.0collides forever) and Cython'sbuild/lib.*/build/temp.*both hand back the previous build../uw petsc switchnow forces the clean rebuild path at switch time:build/lib.*,build/temp.*,build/bdist.*, andbuild/.petsc_targetpip cache purgepetsc4pyin AMR envs (rebuilt by the next./uw buildagainst the new PETSc)3.
pixi.toml→ activation script — makePETSC_ARCHactually change when PETSc changes[feature.*.activation.env] PETSC_ARCH = "petsc-*-uw-*"was pinned in trackedpixi.toml. Since pixi caches its solve againstpixi.tomlcontent,$PETSC_ARCHinside a pixi shell could disagree withpetsc-custom/.petsc-versionwhenever the latter changed.Replace the five hardcoded entries (features
amr[osx-arm64 / linux-64],amr-mpich,amr-openmpi,amr-debug) with a single activation script wired in per-feature:The script (sourced on every
pixi shell/pixi run) readspetsc-custom/.petsc-versionplus$PIXI_ENVIRONMENT_NAME(andunamefor the genericamrenv) and exportsPETSC_ARCH=petsc-{ver}-uw-{mpi}{suffix}. Non-AMR envs match no case and leavePETSC_ARCHunset — no-op fordefault/runtime/dev/mpich/openmpi.pixi.tomlnow stays static across PETSc versions; switching touches only the gitignored.petsc-version.How the three fit together
Any single one of these in isolation still leaves a stale-load path; the three together close the loop.
Test plan
./uw petsc switch v3.25.0(or any other tag) then./uw petsc build && ./uw buildproduces anunderworld3importable against the new arch..sois linked against the new PETSc, not the old one:petsc-{new-ver}-uw-openmpi/lib/libpetsc.{new-ver}.dylib, not the previouspetsc-{old-ver}-*.import petsc4py; petsc4py.get_config()post-switch reports the newPETSC_DIR/PETSC_ARCH.git diff pixi.tomlafter a switch is empty.pixi run -e amr-dev env | grep PETSC_ARCHmatches./uw petsc active.pixi run -e default env | grep -c PETSC_ARCHis0(script is a no-op off-AMR).Notes for reviewers
pixi install -e <env>once to pick up the per-feature activation-script wiring../uwstill containspetsc_arch_for_env()for internal diagnostics; it reads the same.petsc-versionso its output stays consistent with the activation script.Underworld development team with AI support from Claude Code