Solve the composite electrode SOH by bracketed rootfind #14651
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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")' |