Skip to content

Support pixi as an alternative environment manager - #861

Open
egpbos wants to merge 1 commit into
mainfrom
pixi-environment
Open

egpbos wants to merge 1 commit into
mainfrom
pixi-environment

Conversation

@egpbos

@egpbos egpbos commented Sep 9, 2026

Copy link
Copy Markdown
Member

Description

Adds a pixi.toml and pixi.lock so PROTEUS can be installed into a pixi environment, and removes the one thing that stopped install.sh from completing in one.

This is additive and opt-in. It is related to #721 but deliberately does not pre-empt the Option A versus Option B decision under discussion there. Nothing here changes the conda installation path, the docs, or install.sh, and a contributor who never runs pixi sees no behavioural difference. If #721 converges on the prefix-based conda design, these three files can be dropped without unwinding anything else.

For the avoidance of doubt, this is a much smaller thing than Option B as sketched in #721. It does not pin Julia in the manifest, set JULIA_DEPOT_PATH per tree, declare the submodules as editable path dependencies, or convert any install.sh phase into a pixi task. Environment variables are untouched and still come from install.sh and the shell rc file; isolating those is left to a follow-up, since it is the part that actually overlaps #721.

is related to #721

What is in the manifest, and why

Five packages:

  • python = "3.12.*", matching the requires-python ceiling.
  • pip, because a conda-forge python does not pull it in and install.sh calls pip install -e directly. Without it phase 6 fails with pip: command not found.
  • openssl = ">=3.5", which juliacall needs in order to match the Julia 1.12 OpenSSL_jll. install.sh repairs this with conda install when it can; declaring it means the repair is never needed.
  • sundials = ">=7,<8", supplying the SUNDIALS C library that scikits-odes-sundials builds against.

hdf5 and libnetcdf are deliberately not declared. install.sh switches those to no-MPI builds under conda because the MPI builds load libmpi before Julia and the duplicate MPI symbols crash the juliacall bridge. That cannot arise here: pixi list shows neither package in the environment, because the netCDF4 wheel carries its own copies (it reports libnetcdf 4.9.3 and hdf5 1.14.6 from bundled libraries). Declaring them would reintroduce the packages for no benefit.

The one script change

tools/get_cvode.sh installed SUNDIALS with conda install --prefix "$CONDA_PREFIX". Pixi sets both CONDA_DEFAULT_ENV and CONDA_PREFIX, so the script's guard passes, but there is no conda binary, so the command failed and set -euo pipefail exited 1. install.sh downgrades that to a warning, so the install "succeeded" while leaving Aragog on the scipy Radau fallback, which is slower and step-size-fragile on multi-Myr coupled cooling runs. That is a silent correctness-relevant downgrade, not a cosmetic failure.

The script now installs SUNDIALS only when the prefix does not already provide it. When SUNDIALS is absent the new branch is false and the original conda install line runs verbatim, so the conda path is unchanged.

Validation of changes

Test configuration: macOS 26.6.2 on Apple Silicon (osx-arm64), Python 3.12.14, GCC 16.2.0, netCDF-Fortran 4.6.4, Julia 1.12.7, pixi 0.79.0.

Installed from scratch into a fresh clone with pixi run bash install.sh --no-data, pointing FWL_DATA at an existing data directory:

  1. install.sh completed all 8 phases, exit 0. It required no modification; the conda-environment gate in phase 1 passes because pixi sets CONDA_DEFAULT_ENV.
  2. proteus doctor: all checks passed. Every submodule resolved as an editable install (CALLIOPE, JANUS, MORS, ZEPHYRUS, aragog, Zalmoxis), AGNI 1.12.0, SOCRATES 2603.8.
  3. Unit suite, the same filter CI uses: 2966 passed, 41 skipped, 0 failed in 33 s. The skips are SPIDER and the atmodeller / torch / botorch extras, none of which [develop] installs.
  4. Smoke tier: 24 passed, 22 skipped, 0 failed.
  5. proteus start --offline -c input/dummy.toml: exit 0 in 19 s.
  6. CVODE, after the get_cvode.sh change: scikits-odes-sundials builds against the manifest's SUNDIALS and imports, so Aragog keeps its production integration path instead of falling back. The script's own hard import gate confirms this.
  7. AGNI coupling through the juliacall bridge, with PROTEUS_CI_NIGHTLY=1: test_integration_agni_transparent_limit.py and test_integration_agni_greygas_interior.py, 4 passed. Three of those assert the analytic transparent-limit result, that outgoing longwave radiation equals the surface blackbody emission, so radiative transfer is producing correct values and not merely loading.

Not verified, stated explicitly:

  • The conda path was not re-run. No conda, mamba or micromamba is installed on the test machine. The argument that conda behaviour is unchanged rests on the structure of the diff, not on an observed run. The macOS CI job exercises that path and should be treated as the real check.
  • linux-64 is declared and resolves in the lock file, but has not been run. It was added so the manifest is usable on the clusters; treat cluster support as unproven until someone runs it there. Nothing about the cluster questions raised in Install script should not install Julia #721 (package-cache location, file-count quotas, parallel-filesystem behaviour) is answered here.
  • Reaching an AGNI-capable install on GCC 16.2 additionally required Export real_kind_bytes as public to fix dlsym visibility on GCC 16.2 SOCRATES#29 and Create CODE_OF_CONDUCT.md #30. Those are merged upstream but PROTEUS still pins SOCRATES at f42ddec0 (2603.8), so the pin needs bumping separately; that is not part of this PR.

Checklist

  • I have followed the contributing guidelines
  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • My changes generate no new warnings or errors
  • I have checked that the tests still pass on my computer
  • I have updated the docs, as appropriate
  • I have added tests for these changes, as appropriate
  • I have checked that all dependencies have been updated, as required

Docs are intentionally not updated. Documenting pixi as a supported installation route would present the #721 decision as settled, which it is not. If this lands, the docs change belongs with whatever #721 converges on.

No tests are added. The change is a dependency manifest plus a guard in an installer script; the existing suites above are what exercise it, and there is no unit-testable surface. tools/ is exempt from the physics-invariant requirement.

@egpbos
egpbos requested a review from a team as a code owner September 9, 2026 10:02
pixi sets CONDA_DEFAULT_ENV and CONDA_PREFIX, so install.sh runs against
a pixi environment unmodified. What such an environment does not have is
a conda binary, so get_cvode.sh failed at the SUNDIALS install step and
install.sh downgraded that to a warning, silently leaving Aragog on the
step-size-fragile scipy Radau fallback. The manifest declares sundials,
and the script now installs it only when the prefix does not already
provide it, which leaves the conda path unchanged.

pip is declared because a conda-forge python does not pull it in and
install.sh calls pip directly. openssl is held at 3.5 or newer, which
juliacall needs to match the Julia 1.12 OpenSSL_jll. hdf5 and libnetcdf
are deliberately absent: the netCDF4 wheel carries its own, so the MPI
build conflict that install.sh repairs under conda cannot arise.

Environment variables stay with install.sh and the shell rc file, so
nothing changes for existing conda installations.

Verified on osx-arm64: proteus doctor reports all checks passed, the
unit suite is green at 2966 passed, and the AGNI transparent-limit and
grey-gas coupling tests pass. linux-64 is declared and resolves in the
lock file but has not been run.
@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.58%. Comparing base (b06837e) to head (b282597).
⚠️ Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #861      +/-   ##
==========================================
- Coverage   93.13%   91.58%   -1.55%     
==========================================
  Files         112      113       +1     
  Lines       16715    18400    +1685     
  Branches     2987     3409     +422     
==========================================
+ Hits        15567    16852    +1285     
- Misses       1148     1459     +311     
- Partials        0       89      +89     
Flag Coverage Δ
unit-tests 87.25% <ø> (+0.52%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nichollsh

Copy link
Copy Markdown
Member

@egpbos could you clarify the motivation for this PR? Do we need to support both pixi and the current approach - or should we just have this one and deprecate the other?

Also, please can you test the Linux case to ensure that it also functions as expected?

@egpbos

egpbos commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

Motivation is basically that I wanted to try it out just to see how much it would change, both as a possible ramp-up to the solutions we came up with in #721 but also simply because pixi is gaining traction and I just wanted to try it out myself on my new laptop :) Since it turned out to need only very minimal changes (because pixi envs are conda backwards compatible, e.g. they also define conda environment variables if none exist, allowing scripts (like ours) that depend on those to just treat it as a conda env) I thought we could just support it, since it's basically for free.

Regarding #721: pixi environments also have a convenient built-in mechanism for defining other parts of your environment, like the RAD_DIR, FWLDATA and other env vars we currently leak into shell rc. Pixi thus makes it much easier to completely isolate multiple PROTEUS installs (+ deps) on one machine.

Also, please can you test the Linux case to ensure that it also functions as expected?

In progress! (This was how I found the missing netcdf-fortran on Kapteyn, now trying on Habrok instead.)

@timlichtenberg

Copy link
Copy Markdown
Member

I am confused, didn't we in some issue discuss that we do not want go with pixi?

@egpbos

egpbos commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

The discussion in #721 was not conclusive, I thought. We discussed two options there. What I'm going for here is really besides both options (it doesn't solve the Julia or env overreach issues directly). The main find here is that supporting pixi doesn't need much change in the setup at all (see the commit diff, most lines are in the lock file which is updated automatically by pixi; in fact, we could keep it out, that works too). That means that we simply give users an alternative to conda without any maintenance cost. We already almost were supporting it.

I do foresee that a next step could be to solve the env overreach by delegating that to pixi (when pixi is used, with a fallback to the current behavior).

I could also try that out as part of this PR, so we can see the whole thing in one go and then judge whether it works for us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants