Skip to content

Solve the composite electrode SOH by bracketed rootfind #14651

Solve the composite electrode SOH by bracketed rootfind

Solve the composite electrode SOH by bracketed rootfind #14651

Workflow file for this run

name: PyBaMM
on:
pull_request:
# Run on pushes to main as well, primarily to warm the solver-wheel cache
# (keyed on the solver source hash) so PyBaMM-only PRs get a cache hit instead
# of rebuilding the solver. Also gives a post-merge health signal for main.
push:
branches:
- main
permissions: {}
env:
PYBAMM_DISABLE_TELEMETRY: "true"
FORCE_COLOR: 3
concurrency:
# github.workflow: name of the workflow, so that we don't cancel other workflows
# github.event.pull_request.number || github.ref: pull request number or branch name if not a pull request
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
# Cancel in-progress runs when a new workflow with the same group name is triggered
# This avoids workflow runs on both pushes and PRs
cancel-in-progress: true
jobs:
# Dependency-aware routing. `solver`/`pybamm`/`docs` say which subsystems a PR
# touches so we only run (and only build the solver for) what's needed.
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
contents: read
outputs:
solver: ${{ steps.filter.outputs.solver }}
pybamm: ${{ steps.filter.outputs.pybamm }}
docs: ${{ steps.filter.outputs.docs }}
# Routing decisions, resolved once here rather than repeated in every job's `if`.
run_tests: ${{ github.event_name == 'push' || steps.filter.outputs.pybamm == 'true' || steps.filter.outputs.solver == 'true' }}
run_docs_tests: ${{ github.event_name == 'push' || steps.filter.outputs.pybamm == 'true' || steps.filter.outputs.solver == 'true' || steps.filter.outputs.docs == 'true' }}
# Solver-wheel platforms each scenario's downstream jobs actually consume.
platforms: ${{ steps.route.outputs.platforms }}
macos_runners: ${{ steps.route.outputs.macos_runners }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
id: filter
with:
filters: |
solver:
- 'packages/pybammsolvers/**'
- '.github/workflows/_build_solver_wheels.yml'
pybamm:
- 'packages/pybamm/**'
- 'pyproject.toml'
- 'uv.lock'
- 'noxfile.py'
- 'conftest.py'
- '.github/workflows/test_on_push.yml'
# _nox.yml is where the tests actually run, so a change to it must
# route to the test jobs or they would all skip and pass the gate.
- '.github/workflows/_nox.yml'
docs:
- 'docs/**'
# Map "what changed" to the solver wheels to build: solver PRs validate every
# platform; pybamm/push need the sparse matrix's runners; docs-only needs Linux.
- id: route
env:
EVENT: ${{ github.event_name }}
SOLVER: ${{ steps.filter.outputs.solver }}
PYBAMM: ${{ steps.filter.outputs.pybamm }}
run: |
if [ "$SOLVER" = true ]; then
platforms='["linux", "windows"]'
macos='["macos-15-intel", "macos-latest"]'
elif [ "$EVENT" = push ] || [ "$PYBAMM" = true ]; then
platforms='["linux", "windows"]'
macos='["macos-latest"]'
else
platforms='["linux"]'
macos='[]'
fi
{
echo "platforms=$platforms"
echo "macos_runners=$macos"
} >> "$GITHUB_OUTPUT"
style:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.12
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "latest"
enable-cache: true
- name: Check style
run: uvx pre-commit run -a
- name: Check uv.lock is up to date
run: uv lock --check
# Build the in-repo solver once per platform (cibuildwheel, incl. Windows via
# vcpkg) and cache the wheels on the solver source hash. PyBaMM's matrix below
# installs these prebuilt wheels instead of rebuilding the solver per cell.
# The reusable workflow's CIBW_TEST_COMMAND also runs the solver's own pytest
# on every platform, which (with the PyBaMM matrix) supersedes the solver's
# old standalone unit/integration workflows.
build_solver:
needs: changes
if: ${{ needs.changes.outputs.run_docs_tests == 'true' }}
# Pass contents:read down to the called workflow (this workflow's top-level
# permissions are {}, so the reusable workflow's checkout would otherwise
# get no token scope).
permissions:
contents: read
uses: ./.github/workflows/_build_solver_wheels.yml
with:
cache_wheels: true
build_sdist: false
# Build only the wheels the triggered jobs consume (see the changes job's route step).
platforms: ${{ needs.changes.outputs.platforms }}
macos_runners: ${{ needs.changes.outputs.macos_runners }}
# Gated by ci_gate. Full Python on x86 Linux + min/max endpoints on ARM Linux and
# macOS; interior versions run nightly (run_periodic_tests.yml). ubuntu-latest/3.13
# is absent here because it runs the coverage and integration jobs below instead.
run_unit_tests:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Tests
sessions: unit
legs: >-
[{"os": "ubuntu-latest", "python": "3.10"},
{"os": "ubuntu-latest", "python": "3.11"},
{"os": "ubuntu-latest", "python": "3.12"},
{"os": "ubuntu-latest", "python": "3.14"},
{"os": "ubuntu-24.04-arm", "python": "3.10"},
{"os": "ubuntu-24.04-arm", "python": "3.14"},
{"os": "macos-latest", "python": "3.10"},
{"os": "macos-latest", "python": "3.14"}]
# Advisory: reports red on failure but is deliberately absent from ci_gate's
# needs, so a flaky Windows run never blocks a merge.
run_unit_tests_advisory:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Tests
sessions: unit
legs: >-
[{"os": "windows-latest", "python": "3.10"},
{"os": "windows-latest", "python": "3.14"}]
run_coverage:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
job_name: Coverage
sessions: coverage
upload_coverage: true
legs: '[{"os": "ubuntu-latest", "python": "3.13"}]'
run_integration_tests:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Integration tests
sessions: integration
legs: '[{"os": "ubuntu-latest", "python": "3.13"}]'
# `uv sync` based so the solver's auto-bootstrap fires.
# Guards both the bootstrap and the clean-clone dev experience.
from_source_smoke:
needs: changes
if: ${{ github.event_name == 'push' || needs.changes.outputs.solver == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
name: From-source solver smoke (ubuntu-latest / Python 3.13)
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: 'recursive'
persist-credentials: false
# Plain apt (not the cache action): the from-source SuiteSparse build needs
# libblas.so's update-alternatives symlink, which apt file-tree caching drops.
- name: Install Linux system dependencies
run: |
sudo apt-get update
sudo apt-get install -y gfortran gcc make cmake libopenblas-dev
- name: Set up Python 3.13
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.13
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "latest"
enable-cache: true
- name: From-source uv sync (auto-bootstraps SUNDIALS) + IDAKLU smoke
run: |
uv sync --extra all --group dev
uv run python -m pytest packages/pybamm/tests/unit/test_solvers/test_idaklu_solver.py -q
run_doctests:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_docs_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Doctests
sessions: "doctests docs"
# Full clone: the docs build's sphinx_last_updated_by_git needs the history.
fetch_depth: 0
legs: '[{"os": "ubuntu-latest", "python": "3.13"}]'
run_example_tests:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_docs_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Example notebooks
sessions: examples
legs: '[{"os": "ubuntu-latest", "python": "3.13"}]'
run_scripts_tests:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Example scripts
sessions: scripts
legs: '[{"os": "ubuntu-latest", "python": "3.13"}]'
run_memory_tests:
needs: [changes, build_solver]
if: ${{ needs.changes.outputs.run_tests == 'true' }}
permissions:
contents: read
uses: ./.github/workflows/_nox.yml
with:
job_name: Memory tests
sessions: memory
texlive: false
legs: '[{"os": "ubuntu-latest", "python": "3.13"}]'
# Single required check for branch protection. Green when every gated job either
# succeeded or was routed around by `changes`; red on any failure or cancellation.
ci_gate:
if: always()
needs:
- changes
- style
- build_solver
- run_unit_tests
- run_coverage
- run_integration_tests
- from_source_smoke
- run_doctests
- run_example_tests
- run_scripts_tests
- run_memory_tests
runs-on: ubuntu-latest
permissions: {}
name: CI gate
steps:
- name: Check gated job results
env:
RESULTS: ${{ toJSON(needs) }}
run: |
echo "$RESULTS"
echo "$RESULTS" | jq -e 'all(.[].result; . == "success" or . == "skipped")'