diff --git a/.github/workflows/benchmark_on_push.yml b/.github/workflows/benchmark_on_push.yml index 2883eb5f26..23b7378714 100644 --- a/.github/workflows/benchmark_on_push.yml +++ b/.github/workflows/benchmark_on_push.yml @@ -28,7 +28,7 @@ jobs: - name: Install python dependencies run: | - python -m uv pip install --upgrade pip wheel setuptools wget cmake casadi numpy + python -m uv pip install --upgrade pip scikit-build-core wget cmake casadi numpy python -m uv pip install asv[virtualenv] - name: Install SuiteSparse and SUNDIALS diff --git a/.github/workflows/periodic_benchmarks.yml b/.github/workflows/periodic_benchmarks.yml index 14f97c55d8..3b4fd23059 100644 --- a/.github/workflows/periodic_benchmarks.yml +++ b/.github/workflows/periodic_benchmarks.yml @@ -36,7 +36,7 @@ jobs: - name: Install python dependencies run: | - python -m uv pip install --upgrade pip wheel setuptools wget cmake casadi numpy + python -m uv pip install --upgrade pip scikit-build-core wget cmake casadi numpy python -m uv pip install asv[virtualenv] - name: Install SuiteSparse and SUNDIALS @@ -46,9 +46,6 @@ jobs: run: | asv machine --machine "GitHubRunner" asv run --machine "GitHubRunner" NEW --show-stderr -v - env: - SUNDIALS_INST: $HOME/.local - LD_LIBRARY_PATH: $HOME/.local/lib - name: Upload results as artifact uses: actions/upload-artifact@v4.4.3 diff --git a/.github/workflows/publish_pypi.yml b/.github/workflows/publish_pypi.yml index 64c8a3a777..20699ecc48 100644 --- a/.github/workflows/publish_pypi.yml +++ b/.github/workflows/publish_pypi.yml @@ -15,6 +15,8 @@ on: description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' required: false default: false + pull_request: # Temporarily trigger on PRs for debugging, remove later + types: [opened, synchronize, reopened] # Set options available for all jobs that use cibuildwheel env: @@ -47,9 +49,6 @@ jobs: with open(output_file, "a", encoding="utf-8") as output_stream: output_stream.write(f"count={num_cpus}\n") - - name: Clone pybind11 repo (no history) - run: git clone --depth 1 --branch v2.12.0 https://github.com/pybind/pybind11.git -c advice.detachedHead=false - - name: Install vcpkg on Windows run: | cd C:\ @@ -72,7 +71,10 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Build 64-bit wheels on Windows - run: pipx run cibuildwheel --output-dir wheelhouse + uses: pypa/cibuildwheel@v2.19.2 + with: + package-dir: . + output-dir: wheelhouse env: CIBW_ENVIRONMENT: > PYBAMM_USE_VCPKG=ON @@ -83,7 +85,7 @@ jobs: CMAKE_GENERATOR_PLATFORM=x64 CMAKE_BUILD_PARALLEL_LEVEL=${{ steps.get_num_cores.outputs.count }} CIBW_ARCHS: AMD64 - CIBW_BEFORE_BUILD: python -m pip install setuptools wheel delvewheel # skip CasADi and CMake + CIBW_BEFORE_BUILD: python -m pip install scikit-build-core delvewheel pybind11 # skip CasADi and CMake # Fix access violation because GHA runners have modified PATH that picks wrong # msvcp140.dll, see https://github.com/adang1345/delvewheel/issues/54 CIBW_REPAIR_WHEEL_COMMAND: delvewheel repair --add-path C:/Windows/System32 -w {dest_dir} {wheel} @@ -110,17 +112,19 @@ jobs: with: python-version: 3.11 - - name: Clone pybind11 repo (no history) - run: git clone --depth 1 --branch v2.12.0 https://github.com/pybind/pybind11.git -c advice.detachedHead=false - - name: Build wheels on Linux - run: pipx run cibuildwheel --output-dir wheelhouse + uses: pypa/cibuildwheel@v2.19.2 + with: + package-dir: . + output-dir: wheelhouse env: CIBW_ARCHS_LINUX: x86_64 - CIBW_BEFORE_ALL_LINUX: > - yum -y install openblas-devel lapack-devel && - bash scripts/install_sundials.sh 6.0.3 6.5.0 - CIBW_BEFORE_BUILD_LINUX: python -m pip install cmake casadi setuptools wheel + CIBW_BEFORE_BUILD_LINUX: | + set -e -x + yum update -y + yum -y install openblas-devel + python scripts/install_KLU_Sundials.py + python -m pip install cmake casadi scikit-build-core pybind11 CIBW_REPAIR_WHEEL_COMMAND_LINUX: auditwheel repair -w {dest_dir} {wheel} CIBW_TEST_EXTRAS: "all,dev,jax" CIBW_TEST_COMMAND: | @@ -148,9 +152,6 @@ jobs: with: python-version: '3.11' - - name: Clone pybind11 repo (no history) - run: git clone --depth 1 --branch v2.12.0 https://github.com/pybind/pybind11.git -c advice.detachedHead=false - - name: Install cibuildwheel run: python -m pip install cibuildwheel @@ -241,11 +242,12 @@ jobs: python scripts/install_KLU_Sundials.py python -m cibuildwheel --output-dir wheelhouse + env: # 10.13 for Intel (macos-12/macos-13), 11.0 for Apple Silicon (macos-14 and macos-latest) MACOSX_DEPLOYMENT_TARGET: ${{ matrix.os == 'macos-14' && '11.0' || '10.13' }} CIBW_ARCHS_MACOS: auto - CIBW_BEFORE_BUILD: python -m pip install cmake casadi setuptools wheel delocate + CIBW_BEFORE_BUILD: python -m pip install cmake casadi scikit-build-core delocate CIBW_REPAIR_WHEEL_COMMAND: | if [[ $(uname -m) == "x86_64" ]]; then delocate-listdeps {wheel} && delocate-wheel -v -w {dest_dir} {wheel} @@ -289,7 +291,10 @@ jobs: publish_pypi: # This job is only of value to PyBaMM and would always be skipped in forks - if: github.event_name != 'schedule' && github.repository == 'pybamm-team/PyBaMM' + if: >- + (github.event_name == 'workflow_dispatch' || + github.event_name == 'release') && + github.repository == 'pybamm-team/PyBaMM' name: Upload package to PyPI needs: [ build_manylinux_wheels, diff --git a/.github/workflows/run_periodic_tests.yml b/.github/workflows/run_periodic_tests.yml index 68a8a1ae8b..9636c30f6e 100644 --- a/.github/workflows/run_periodic_tests.yml +++ b/.github/workflows/run_periodic_tests.yml @@ -26,27 +26,66 @@ concurrency: cancel-in-progress: true jobs: - run_tests: - runs-on: ${{ matrix.os }} + run_windows_unit_integration_tests: + env: + PYBAMM_USE_VCPKG: ON + VCPKG_ROOT_DIR: C:\vcpkg + VCPKG_DEFAULT_TRIPLET: x64-windows-static-md + VCPKG_FEATURE_FLAGS: manifests,registries + CMAKE_GENERATOR: "Visual Studio 17 2022" + runs-on: windows-latest strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12, macos-14, windows-latest ] - python-version: [ "3.9", "3.10", "3.11", "3.12" ] - name: Tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) + python-version: ["3.9", "3.10", "3.11", "3.12"] + name: Tests (windows-latest / Python ${{ matrix.python-version }}) steps: - name: Check out PyBaMM repository uses: actions/checkout@v4 - - name: Install Linux system dependencies - if: matrix.os == 'ubuntu-latest' + - name: Install Windows system dependencies + run: choco install graphviz --version=8.0.5 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install vcpkg on Windows run: | - sudo apt-get update - sudo apt-get install gfortran gcc graphviz pandoc libopenblas-dev texlive-latex-extra dvipng + cd C:\ + rm -r -fo 'C:\vcpkg' + git clone https://github.com/microsoft/vcpkg + cd vcpkg + .\bootstrap-vcpkg.bat + + - name: Install nox + run: python -m pip install nox + + - name: Run unit tests for windows-latest with Python ${{ matrix.python-version }} + run: python -m nox -s unit + + - name: Run integration tests for windows-latest with Python ${{ matrix.python-version }} + run: python -m nox -s integration + + run_macos_unit_integration_tests: + runs-on: ${{ matrix.os }} + env: + PYBAMM_IDAKLU_EXPR_CASADI: ON + PYBAMM_IDAKLU_EXPR_IREE: ON + strategy: + fail-fast: false + matrix: + os: [macos-12, macos-14] + python-version: ["3.9", "3.10", "3.11", "3.12"] + name: Tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) + + steps: + - name: Check out PyBaMM repository + uses: actions/checkout@v4 - name: Install macOS system dependencies - if: matrix.os == 'macos-12' || matrix.os == 'macos-14' env: HOMEBREW_NO_INSTALL_CLEANUP: 1 HOMEBREW_NO_AUTO_UPDATE: 1 @@ -59,9 +98,38 @@ jobs: brew install graphviz libomp brew reinstall gcc - - name: Install Windows system dependencies - if: matrix.os == 'windows-latest' - run: choco install graphviz --version=8.0.5 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install nox + run: python -m pip install nox + + - name: Install SuiteSparse and SUNDIALS on macOS + timeout-minutes: 10 + run: python -m nox -s pybamm-requires + + - name: Run unit tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} + run: python -m nox -s unit + + - name: Run integration tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} + run: python -m nox -s integration + + run_linux_unit_integration_coverage_tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + name: Tests (ubuntu-latest / Python ${{ matrix.python-version }}) + + steps: + - name: Check out PyBaMM repository + uses: actions/checkout@v4 + + - name: Install Linux system dependencies + run: sudo apt install gfortran gcc graphviz pandoc libopenblas-dev texlive-latex-extra dvipng - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 @@ -74,31 +142,34 @@ jobs: - name: Install nox run: python -m uv pip install nox[uv] - - name: Install SuiteSparse and SUNDIALS on GNU/Linux and macOS + - name: Install SuiteSparse and SUNDIALS on GNU/Linux timeout-minutes: 10 - if: matrix.os != 'windows-latest' run: python -m nox -s pybamm-requires - - name: Run unit tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} - if: matrix.os != 'ubuntu-latest' || matrix.python-version != '3.12' + - name: Run unit tests for GNU/Linux with Python ${{ matrix.python-version }} + if: matrix.python-version != '3.12' run: python -m nox -s unit - - name: Run coverage tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' + - name: Run coverage tests for GNU/Linux with Python ${{ matrix.python-version }} + if: matrix.python-version == '3.12' run: python -m nox -s coverage - name: Upload coverage report - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' + if: matrix.python-version == '3.12' uses: codecov/codecov-action@v4.6.0 with: token: ${{ secrets.CODECOV_TOKEN }} - - name: Run integration tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} + - name: Run integration tests for GNU/Linux with Python ${{ matrix.python-version }} run: python -m nox -s integration # Skips IDAKLU module compilation for speedups, which is already tested in other jobs. run_doctests: + env: + BUILD_IDAKLU: OFF runs-on: ubuntu-latest + strategy: + fail-fast: false name: Doctests (ubuntu-latest / Python 3.11) steps: @@ -108,9 +179,7 @@ jobs: fetch-depth: 0 - name: Install Linux system dependencies - run: | - sudo apt-get update - sudo apt-get install graphviz pandoc texlive-latex-extra dvipng + run: sudo apt install graphviz pandoc texlive-latex-extra dvipng - name: Set up Python uses: actions/setup-python@v5 @@ -131,6 +200,8 @@ jobs: run_example_tests: runs-on: ubuntu-latest + strategy: + fail-fast: false name: Example notebooks (ubuntu-latest / Python 3.12) steps: @@ -138,9 +209,7 @@ jobs: uses: actions/checkout@v4 - name: Install Linux system dependencies - run: | - sudo apt-get update - sudo apt-get install gfortran gcc graphviz pandoc libopenblas-dev texlive-latex-extra dvipng + run: sudo apt install gfortran gcc graphviz pandoc libopenblas-dev texlive-latex-extra dvipng - name: Set up Python 3.12 uses: actions/setup-python@v5 @@ -162,6 +231,8 @@ jobs: run_scripts_tests: runs-on: ubuntu-latest + strategy: + fail-fast: false name: Example scripts (ubuntu-latest / Python 3.12) steps: @@ -169,9 +240,7 @@ jobs: uses: actions/checkout@v4 - name: Install Linux system dependencies - run: | - sudo apt-get update - sudo apt install gfortran gcc graphviz libopenblas-dev texlive-latex-extra dvipng + run: sudo apt install gfortran gcc graphviz libopenblas-dev texlive-latex-extra dvipng - name: Set up Python 3.12 uses: actions/setup-python@v5 diff --git a/.github/workflows/test_on_push.yml b/.github/workflows/test_on_push.yml index 9be0c7b3ea..7115a593a4 100644 --- a/.github/workflows/test_on_push.yml +++ b/.github/workflows/test_on_push.yml @@ -35,37 +35,78 @@ jobs: python -m uv pip install pre-commit pre-commit run -a - run_unit_integration_and_coverage_tests: + run_windows_unit_integration_tests: needs: style - runs-on: ${{ matrix.os }} + env: + PYBAMM_USE_VCPKG: ON + VCPKG_ROOT_DIR: C:\vcpkg + VCPKG_DEFAULT_TRIPLET: x64-windows-static-md + VCPKG_FEATURE_FLAGS: manifests,registries + CMAKE_GENERATOR: "Visual Studio 17 2022" + runs-on: windows-latest strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-12, macos-14, windows-latest] python-version: ["3.9", "3.10", "3.11", "3.12"] - name: Tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) + name: Tests (windows-latest / Python ${{ matrix.python-version }}) steps: - name: Check out PyBaMM repository uses: actions/checkout@v4 - - name: Install Linux system dependencies - uses: awalsh128/cache-apt-pkgs-action@v1.4.2 - if: matrix.os == 'ubuntu-latest' + - name: Install Windows system dependencies + run: choco install graphviz --version=8.0.5 + + - name: Set up Python ${{ matrix.python-version }} + id: setup-python + uses: actions/setup-python@v5 with: - packages: gfortran gcc graphviz pandoc - execute_install_scripts: true + python-version: ${{ matrix.python-version }} + cache: 'pip' + + - name: Cache packages installed through vcpkg on Windows + uses: actions/cache@v4 + env: + cache-name: vcpkg_binary_cache + with: + path: C:\Users\runneradmin\AppData\Local\vcpkg\archives + key: ${{ runner.os }}-build-VS2022-${{ env.cache-name }}-${{ hashFiles('vcpkg*.json') }}-${{ steps.setup-python.outputs.python-version }} - # dot -c is for registering graphviz fonts and plugins - - name: Install OpenBLAS and TeXLive for Linux - if: matrix.os == 'ubuntu-latest' + - name: Install vcpkg on Windows run: | - sudo apt-get update - sudo dot -c - sudo apt-get install libopenblas-dev texlive-latex-extra dvipng + cd C:\ + rm -r -fo 'C:\vcpkg' + git clone https://github.com/microsoft/vcpkg + cd vcpkg + .\bootstrap-vcpkg.bat + + - name: Install nox + run: python -m pip install nox + + - name: Run unit tests for windows-latest with Python ${{ matrix.python-version }} + run: python -m nox -s unit + + - name: Run integration tests for windows-latest with Python ${{ matrix.python-version }} + run: python -m nox -s integration + + run_macos_unit_integration_tests: + needs: style + runs-on: ${{ matrix.os }} + env: + PYBAMM_IDAKLU_EXPR_CASADI: ON + PYBAMM_IDAKLU_EXPR_IREE: ON + strategy: + fail-fast: false + matrix: + os: [macos-12, macos-14] + python-version: ["3.9", "3.10", "3.11", "3.12"] + name: Tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) + + steps: + - name: Check out PyBaMM repository + uses: actions/checkout@v4 - name: Install macOS system dependencies - if: matrix.os == 'macos-12' || matrix.os == 'macos-14' env: HOMEBREW_NO_INSTALL_CLEANUP: 1 HOMEBREW_NO_AUTO_UPDATE: 1 @@ -78,9 +119,58 @@ jobs: brew install graphviz libomp brew reinstall gcc - - name: Install Windows system dependencies - if: matrix.os == 'windows-latest' - run: choco install graphviz --version=8.0.5 + - name: Set up Python ${{ matrix.python-version }} + id: setup-python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + + - name: Install nox + run: python -m pip install nox + + - name: Cache pybamm-requires nox environment for macOS + uses: actions/cache@v4 + with: + path: | + # Headers and dynamic library files for SuiteSparse and SUNDIALS + ${{ github.workspace }}/download_KLU_Sundials/ + ${{ github.workspace }}/sundials_KLU_libs/ + key: nox-${{ matrix.os }}-pybamm-requires-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/install_KLU_Sundials.py', '**/noxfile.py', '**/test_on_push.yml') }} + + - name: Install SuiteSparse and SUNDIALS on macOS + timeout-minutes: 10 + run: python -m nox -s pybamm-requires + + - name: Run unit tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} + run: python -m nox -s unit + + - name: Run integration tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} + run: python -m nox -s integration + + run_linux_unit_integration_coverage_tests: + needs: style + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + name: Tests (ubuntu-latest / Python ${{ matrix.python-version }}) + + steps: + - name: Check out PyBaMM repository + uses: actions/checkout@v4 + + - name: Install Linux system dependencies + uses: awalsh128/cache-apt-pkgs-action@v1.4.2 + with: + packages: gfortran gcc graphviz pandoc texlive-latex-extra dvipng + execute_install_scripts: true + + - name: Install OpenBLAS for Linux + run: | + sudo apt-get update + sudo apt-get install libopenblas-dev - name: Set up Python ${{ matrix.python-version }} id: setup-python @@ -95,44 +185,41 @@ jobs: - name: Install nox run: python -m uv pip install nox[uv] - - name: Cache pybamm-requires nox environment for GNU/Linux and macOS + - name: Cache pybamm-requires nox environment for GNU/Linux uses: actions/cache@v4 - if: matrix.os != 'windows-latest' with: path: | - # Repository files - ${{ github.workspace }}/pybind11/ - ${{ github.workspace }}/install_KLU_Sundials/ # Headers and dynamic library files for SuiteSparse and SUNDIALS - ${{ env.HOME }}/.local/lib/ - ${{ env.HOME }}/.local/include/ - key: nox-${{ matrix.os }}-pybamm-requires-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/install_KLU_Sundials.py', '**/noxfile.py', '**/test_on_push.yml') }} + ${{ github.workspace }}/download_KLU_Sundials/ + ${{ github.workspace }}/sundials_KLU_libs/ + key: nox-ubuntu-latest-pybamm-requires-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/install_KLU_Sundials.py', '**/noxfile.py', '**/test_on_push.yml') }} - - name: Install SuiteSparse and SUNDIALS on GNU/Linux and macOS + - name: Install SuiteSparse and SUNDIALS on GNU/Linux timeout-minutes: 10 - if: matrix.os != 'windows-latest' run: python -m nox -s pybamm-requires - - name: Run unit tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} - if: matrix.os != 'ubuntu-latest' || matrix.python-version != '3.12' + - name: Run unit tests for GNU/Linux with Python ${{ matrix.python-version }} + if: matrix.python-version != '3.12' run: python -m nox -s unit - - name: Run coverage tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' + - name: Run coverage tests for GNU/Linux with Python ${{ matrix.python-version }} + if: matrix.python-version == '3.12' run: python -m nox -s coverage - name: Upload coverage report - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' + if: matrix.python-version == '3.12' uses: codecov/codecov-action@v4.6.0 with: token: ${{ secrets.CODECOV_TOKEN }} - - name: Run integration tests for ${{ matrix.os }} with Python ${{ matrix.python-version }} + - name: Run integration tests for GNU/Linux with Python ${{ matrix.python-version }} run: python -m nox -s integration # Skips IDAKLU module compilation for speedups, which is already tested in other jobs. run_doctests: needs: style + env: + BUILD_IDAKLU: OFF runs-on: ubuntu-latest strategy: fail-fast: false @@ -147,16 +234,9 @@ jobs: - name: Install Linux system dependencies uses: awalsh128/cache-apt-pkgs-action@v1.4.2 with: - packages: graphviz pandoc + packages: graphviz pandoc texlive-latex-extra dvipng execute_install_scripts: true - # dot -c is for registering graphviz fonts and plugins - - name: Install TeXLive for Linux - run: | - sudo apt-get update - sudo dot -c - sudo apt-get install texlive-latex-extra dvipng - - name: Set up Python id: setup-python uses: actions/setup-python@v5 @@ -190,15 +270,13 @@ jobs: - name: Install Linux system dependencies uses: awalsh128/cache-apt-pkgs-action@v1.4.2 with: - packages: gfortran gcc graphviz pandoc + packages: gfortran gcc graphviz pandoc texlive-latex-extra dvipng execute_install_scripts: true - # dot -c is for registering graphviz fonts and plugins - - name: Install OpenBLAS and TeXLive for Linux + - name: Install OpenBLAS for Linux run: | sudo apt-get update - sudo dot -c - sudo apt-get install libopenblas-dev texlive-latex-extra dvipng + sudo apt-get install libopenblas-dev - name: Set up Python 3.12 id: setup-python @@ -217,13 +295,10 @@ jobs: uses: actions/cache@v4 with: path: | - # Repository files - ${{ github.workspace }}/pybind11/ - ${{ github.workspace }}/install_KLU_Sundials/ # Headers and dynamic library files for SuiteSparse and SUNDIALS - ${{ env.HOME }}/.local/lib/ - ${{ env.HOME }}/.local/include/ - key: nox-${{ matrix.os }}-pybamm-requires-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/install_KLU_Sundials.py', '**/noxfile.py', '**/test_on_push.yml') }} + ${{ github.workspace }}/download_KLU_Sundials/ + ${{ github.workspace }}/sundials_KLU_libs/ + key: nox-ubuntu-latest-pybamm-requires-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/install_KLU_Sundials.py', '**/noxfile.py', '**/test_on_push.yml') }} - name: Install SuiteSparse and SUNDIALS on GNU/Linux timeout-minutes: 10 @@ -246,15 +321,13 @@ jobs: - name: Install Linux system dependencies uses: awalsh128/cache-apt-pkgs-action@v1.4.2 with: - packages: gfortran gcc graphviz + packages: gfortran gcc graphviz libopenblas-dev texlive-latex-extra dvipng execute_install_scripts: true - # dot -c is for registering graphviz fonts and plugins - - name: Install OpenBLAS and TeXLive for Linux + - name: Install OpenBLAS for Linux run: | sudo apt-get update - sudo dot -c - sudo apt-get install libopenblas-dev texlive-latex-extra dvipng + sudo apt-get install libopenblas-dev - name: Set up Python 3.12 id: setup-python @@ -273,12 +346,9 @@ jobs: uses: actions/cache@v4 with: path: | - # Repository files - ${{ github.workspace }}/pybind11/ - ${{ github.workspace }}/install_KLU_Sundials/ # Headers and dynamic library files for SuiteSparse and SUNDIALS - ${{ env.HOME }}/.local/lib/ - ${{ env.HOME }}/.local/include/ + ${{ github.workspace }}/download_KLU_Sundials/ + ${{ github.workspace }}/sundials_KLU_libs/ key: nox-${{ matrix.os }}-pybamm-requires-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/install_KLU_Sundials.py', '**/noxfile.py', '**/test_on_push.yml') }} - name: Install SuiteSparse and SUNDIALS on GNU/Linux diff --git a/.gitignore b/.gitignore index a3bd3b502c..44092b90e6 100644 --- a/.gitignore +++ b/.gitignore @@ -55,8 +55,7 @@ matplotlibrc ideas/ .idea/ -# setup.py files -*.egg-info +# distribution files dist/ # coverage @@ -86,6 +85,14 @@ sundials-* SuiteSparse-* build_sundials KLU_module_deps +sundials_KLU_libs/ +download_KLU_Sundials/ + +# ignore Python Egg Info Directories +*.egg-info + +# ignore IREE +iree # downloads *.gz diff --git a/CMakeLists.txt b/CMakeLists.txt index ec594e5ca5..13071c20d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,14 +32,17 @@ endif() # casadi seems to compile without the newer versions of std::string add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) -if(NOT PYBIND11_DIR) - set(PYBIND11_DIR pybind11) -endif() -add_subdirectory(${PYBIND11_DIR}) +find_package(pybind11 CONFIG REQUIRED) + +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) # Check Casadi build flag if(NOT DEFINED PYBAMM_IDAKLU_EXPR_CASADI) - set(PYBAMM_IDAKLU_EXPR_CASADI ON) + if(DEFINED ENV{PYBAMM_IDAKLU_EXPR_CASADI}) + set(PYBAMM_IDAKLU_EXPR_CASADI "$ENV{PYBAMM_IDAKLU_EXPR_CASADI}") + else() + set(PYBAMM_IDAKLU_EXPR_CASADI ON) + endif() endif() message("PYBAMM_IDAKLU_EXPR_CASADI: ${PYBAMM_IDAKLU_EXPR_CASADI}") @@ -55,7 +58,11 @@ endif() # Check IREE build flag if(NOT DEFINED PYBAMM_IDAKLU_EXPR_IREE) - set(PYBAMM_IDAKLU_EXPR_IREE OFF) + if(DEFINED ENV{PYBAMM_IDAKLU_EXPR_IREE}) + set(PYBAMM_IDAKLU_EXPR_IREE "$ENV{PYBAMM_IDAKLU_EXPR_IREE}") + else() + set(PYBAMM_IDAKLU_EXPR_IREE OFF) + endif() endif() message("PYBAMM_IDAKLU_EXPR_IREE: ${PYBAMM_IDAKLU_EXPR_IREE}") @@ -74,7 +81,6 @@ if(${PYBAMM_IDAKLU_EXPR_IREE} STREQUAL "ON" ) ) endif() -# The complete (all dependencies) sources list should be mirrored in setup.py pybind11_add_module(idaklu # pybind11 interface src/pybamm/solvers/c_solvers/idaklu.cpp @@ -112,7 +118,9 @@ pybind11_add_module(idaklu ${IDAKLU_EXPR_IREE_SOURCE_FILES} ) -if (NOT DEFINED USE_PYTHON_CASADI) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(USE_PYTHON_CASADI FALSE) +else() set(USE_PYTHON_CASADI TRUE) endif() @@ -124,7 +132,7 @@ execute_process( OUTPUT_VARIABLE CASADI_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) -if (CASADI_DIR) +if(CASADI_DIR) file(TO_CMAKE_PATH ${CASADI_DIR} CASADI_DIR) message("Found Python casadi path: ${CASADI_DIR}") endif() @@ -138,19 +146,13 @@ else() find_package(casadi CONFIG REQUIRED) endif() -set_target_properties( - idaklu PROPERTIES - INSTALL_RPATH "${CASADI_DIR}" - INSTALL_RPATH_USE_LINK_PATH TRUE -) - # openmp -if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") execute_process( COMMAND "brew" "--prefix" OUTPUT_VARIABLE HOMEBREW_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) - if (OpenMP_ROOT) + if(OpenMP_ROOT) set(OpenMP_ROOT "${OpenMP_ROOT}:${HOMEBREW_PREFIX}/opt/libomp") else() set(OpenMP_ROOT "${HOMEBREW_PREFIX}/opt/libomp") @@ -162,6 +164,15 @@ if(OpenMP_CXX_FOUND) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) + +if(NOT DEFINED ENV{INSTALL_DIR}) + set(INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sundials_KLU_libs") +else() + set(INSTALL_DIR $ENV{INSTALL_DIR}) +endif() + +message(STATUS "INSTALL_DIR is set to: ${INSTALL_DIR}") + # Sundials find_package(SUNDIALS REQUIRED) message("SUNDIALS found in ${SUNDIALS_INCLUDE_DIR}: ${SUNDIALS_LIBRARIES}") @@ -180,6 +191,21 @@ endif() include_directories(${SuiteSparse_INCLUDE_DIRS}) target_link_libraries(idaklu PRIVATE ${SuiteSparse_LIBRARIES}) +if(DEFINED ENV{CIBUILDWHEEL} AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(SUNDIALS_LIBRARY_DIR "${INSTALL_DIR}/lib64") + set(SUITESPARSE_LIBRARY_DIR "${INSTALL_DIR}/lib64") +else() + set(SUNDIALS_LIBRARY_DIR "${INSTALL_DIR}/lib") + set(SUITESPARSE_LIBRARY_DIR "${INSTALL_DIR}/lib") +endif() + +set_target_properties(idaklu PROPERTIES + INSTALL_RPATH "${CASADI_DIR};${SUNDIALS_LIBRARY_DIR};${SUITESPARSE_LIBRARY_DIR}" + INSTALL_RPATH_USE_LINK_PATH TRUE + BUILD_RPATH "${CASADI_DIR};${SUNDIALS_LIBRARY_DIR};${SUITESPARSE_LIBRARY_DIR}" +) +message("NOTE: RPATHS linking SUNDIALS, SuiteSparse and casADi are set") + # IREE (MLIR compiler and runtime library) build settings if(${PYBAMM_IDAKLU_EXPR_IREE} STREQUAL "ON" ) set(IREE_BUILD_COMPILER ON) @@ -192,3 +218,9 @@ if(${PYBAMM_IDAKLU_EXPR_IREE} STREQUAL "ON" ) target_link_libraries(idaklu PRIVATE iree_compiler_bindings_c_loader) target_link_libraries(idaklu PRIVATE iree_runtime_runtime) endif() + +if(DEFINED ENV{CIBUILDWHEEL}) + install(TARGETS idaklu DESTINATION "pybamm/solvers") +else() + install(TARGETS idaklu DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/src/pybamm/solvers") +endif() diff --git a/FindSUNDIALS.cmake b/FindSUNDIALS.cmake index 4f8c52b64f..d3b2d0147d 100644 --- a/FindSUNDIALS.cmake +++ b/FindSUNDIALS.cmake @@ -41,6 +41,7 @@ find_path(SUNDIALS_INCLUDE_DIR include PATHS ${SUNDIALS_ROOT} + ${INSTALL_DIR} ) set(SUNDIALS_WANT_COMPONENTS @@ -72,8 +73,9 @@ foreach(LIB ${SUNDIALS_WANT_COMPONENTS}) PATH_SUFFIXES lib Lib - PATHS - ${SUNDIALS_ROOT} + PATHS + ${SUNDIALS_ROOT} + ${INSTALL_DIR} ) set(SUNDIALS_${LIB}_FOUND FALSE) diff --git a/FindSuiteSparse.cmake b/FindSuiteSparse.cmake index c5275c02c0..de75d728d5 100644 --- a/FindSuiteSparse.cmake +++ b/FindSuiteSparse.cmake @@ -37,6 +37,20 @@ # system paths. # +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + enable_language(Fortran) + set(IDAKLU_BLAS_VENDOR + "OpenBLAS" + CACHE + STRING + "Sets the BLAS/LAPACK vendor. See https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors." + ) + set(BLA_VENDOR ${IDAKLU_BLAS_VENDOR}) + + # openBLAS: include a "lib" prefix in its names + set(CMAKE_FIND_LIBRARY_PREFIXES "" lib) +endif() + find_package(BLAS QUIET) # look for desired componenents @@ -60,7 +74,9 @@ endif() # look for library at positions given by the user find_library(SUITESPARSE_CONFIG_LIB NAMES "suitesparseconfig" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "lib" "lib32" "lib64" "Lib" NO_DEFAULT_PATH ) @@ -73,7 +89,9 @@ find_library(SUITESPARSE_CONFIG_LIB #look for header files at positions given by the user find_path(SUITESPARSE_INCLUDE_DIR NAMES "SuiteSparse_config.h" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "SuiteSparse_config" "SuiteSparse_config/include" "suitesparse" "include" "src" "SuiteSparse_config/Include" NO_DEFAULT_PATH ) @@ -89,7 +107,9 @@ foreach(_component ${SUITESPARSE_COMPONENTS}) #look for library at positions given by the user find_library(${_component}_LIBRARY NAMES "${_componentLower}" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "lib" "lib32" "lib64" "${_component}" "${_component}/Lib" NO_DEFAULT_PATH ) @@ -102,7 +122,9 @@ foreach(_component ${SUITESPARSE_COMPONENTS}) #look for header files at positions given by the user find_path(${_component}_INCLUDE_DIR NAMES "${_componentLower}.h" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "${_componentLower}" "include/${_componentLower}" "suitesparse" "include" "src" "${_component}" "${_component}/Include" NO_DEFAULT_PATH ) @@ -117,7 +139,9 @@ endforeach() #look for header files at positions given by the user find_path(SPQR_INCLUDE_DIR NAMES "SuiteSparseQR.hpp" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "spqr" "include/spqr" "suitesparse" "include" "src" "SPQR" "SPQR/Include" NO_DEFAULT_PATH ) diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 6b0e944a4c..0000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,8 +0,0 @@ -graft src -include CITATION.cff -prune tests - -exclude CHANGELOG.md CODE-OF-CONDUCT.md CONTRIBUTING.md all_contributors.md -include CMakeLists.txt - -global-exclude __pycache__ *.py[cod] .venv diff --git a/asv.conf.json b/asv.conf.json index bf9935ce95..5705909aa9 100644 --- a/asv.conf.json +++ b/asv.conf.json @@ -24,8 +24,6 @@ // "install_command": ["in-dir={env_dir} python -mpip install {wheel_file}"], // "uninstall_command": ["return-code=any python -mpip uninstall -y {project}"], "build_command": [ - "/bin/rm -rf pybind11", - "/usr/bin/git clone --depth 1 --branch v2.12.0 https://github.com/pybind/pybind11.git pybind11 -c advice.detachedHead=false", "python -m pip install build", "python -m build --wheel -o {build_cache_dir} {build_dir}" ], @@ -88,10 +86,11 @@ "sympy": [], "bpx": [], "tqdm": [], - "matplotlib": [], + "matplotlib": [] }, "env": { - "LD_LIBRARY_PATH": "/home/runner/.local/lib" + "LD_LIBRARY_PATH": "/home/runner/work/PyBaMM/PyBaMM/sundials_KLU_libs/lib/:${HOME}/.local/lib:${LD_LIBRARY_PATH}", + "INSTALL_DIR": "/home/runner/work/PyBaMM/PyBaMM/sundials_KLU_libs" } } diff --git a/docs/source/user_guide/installation/install-from-source.rst b/docs/source/user_guide/installation/install-from-source.rst index ea664b4a5b..5af6d8f996 100644 --- a/docs/source/user_guide/installation/install-from-source.rst +++ b/docs/source/user_guide/installation/install-from-source.rst @@ -1,5 +1,5 @@ -Install from source (GNU Linux and macOS) -========================================= +Install from source +=================== .. contents:: @@ -12,146 +12,270 @@ Here is a gentle introduction/refresher: `Python Virtual Environments: A Primer Prerequisites --------------- -The following instructions are valid for both GNU/Linux distributions and MacOS. -If you are running Windows, consider using the `Windows Subsystem for Linux (WSL) `_. +.. tab:: Ubuntu/Debian -To obtain the PyBaMM source code, clone the GitHub repository + To install PyBaMM, you will need: -.. code:: bash + - Python 3 (PyBaMM supports versions 3.9, 3.10, 3.11, and 3.12) + - The Python headers file for your current Python version. + - A BLAS library (for instance `openblas `_). + - A C compiler (ex: ``gcc``). + - A Fortran compiler (ex: ``gfortran``). + - ``graphviz`` (optional), if you wish to build the documentation locally. + - ``pandoc`` (optional) to convert the example Jupyter notebooks when building the documentation. + - ``texlive-latex-extra`` (optional) to convert model equations in latex. + - ``dvipng`` (optional) to convert a DVI file to a PNG image. + + You can install the above with - git clone https://github.com/pybamm-team/PyBaMM.git + .. code:: bash -or download the source archive on the repository's homepage. + sudo apt install python3.X python3.X-dev libopenblas-dev gcc gfortran graphviz cmake pandoc texlive-latex-extra dvipng -To install PyBaMM, you will need: + Where ``X`` is the version sub-number. -- Python 3 (PyBaMM supports versions 3.9, 3.10, 3.11, and 3.12) -- The Python headers file for your current Python version. -- A BLAS library (for instance `openblas `_). -- A C compiler (ex: ``gcc``). -- A Fortran compiler (ex: ``gfortran``). -- ``graphviz`` (optional), if you wish to build the documentation locally. -- ``pandoc`` (optional) to convert the example Jupyter notebooks when building the documentation. + .. note:: -You can install the above with + If you are using some other linux distribution you can install the equivalent packages for ``python3, cmake, gcc, gfortran, openblas, pandoc, texlive-latex-extra, dvipng``. -.. tab:: Ubuntu/Debian + Finally, we recommend using `Nox `_. + You can install it to your local user account (make sure you are not within a virtual environment) with .. code:: bash - sudo apt install python3.X python3.X-dev libopenblas-dev gcc gfortran graphviz cmake pandoc + python3.X -m pip install --user nox - Where ``X`` is the version sub-number. + Note that running ``nox`` will create new virtual environments for you to use, so you do not need to create one yourself. + + Depending on your operating system, you may or may not have ``pip`` installed along Python. + If ``pip`` is not found, you probably want to install the ``python3-pip`` package. .. tab:: MacOS + To install PyBaMM, you will need: + + - Python 3 (PyBaMM supports versions 3.9, 3.10, 3.11, and 3.12) + - The Python headers file for your current Python version. + - A BLAS library (for instance `openblas `_). + - A C compiler (ex: ``gcc``). + - A Fortran compiler (ex: ``gfortran``). + - ``graphviz`` (optional), if you wish to build the documentation locally. + - ``pandoc`` (optional) to convert the example Jupyter notebooks when building the documentation. + - ``texlive-latex-extra`` (optional) to convert model equations in latex. + - ``dvipng`` (optional) to convert a DVI file to a PNG image. + + You can install the above with + .. code:: bash brew install python openblas gcc gfortran graphviz libomp cmake pandoc -.. note:: + Finally, we recommend using `Nox `_. + You can install it to your local user account (make sure you are not within a virtual environment) with - If you are using some other linux distribution you can install the equivalent packages for ``python3, cmake, gcc, gfortran, openblas, pandoc``. + .. code:: bash - On Windows, you can install ``graphviz`` using the `Chocolatey `_ package manager, or follow the instructions on the `graphviz website `_. + python3.X -m pip install --user nox -Finally, we recommend using `Nox `_. -You can install it to your local user account (make sure you are not within a virtual environment) with + Note that running ``nox`` will create new virtual environments for you to use, so you do not need to create one yourself. -.. code:: bash + Depending on your operating system, you may or may not have ``pip`` installed along Python. + If ``pip`` is not found, you probably want to install the ``python3-pip`` package. + +.. tab:: Windows + + To use PyBaMM, you must have Python 3.9, 3.10, 3.11, or 3.12 installed. + + To install Python 3.X, download the installation files from `Python’s + website `_. Make sure to + tick the box on ``Add Python 3.X to PATH``. For more detailed + instructions please see the `official Python on Windows + guide `__. + + (Optional) If you wish to build the documentation locally, you can install ``graphviz`` using the `Chocolatey `_ package manager, + + .. code:: bash - python3.X -m pip install --user nox + choco install graphviz -Note that running ``nox`` will create new virtual environments for you to use, so you do not need to create one yourself. + or follow the instructions on the `graphviz website `_. -Depending on your operating system, you may or may not have ``pip`` installed along Python. -If ``pip`` is not found, you probably want to install the ``python3-pip`` package. + Finally, we recommend using `Nox `_. + You can install it to your local user account (make sure you are not within a virtual environment) with + + .. code:: bash + + python -m pip install --user nox + + Note that running ``nox`` will create new virtual environments for you to use, so you do not need to create one yourself. + + Please note that all ``nox`` commands should now be run with ``python -m`` prepended. For example, instead of using ``nox -s dev``, use ``python -m nox -s dev``. + +.. _install-build-time: Installing the build-time requirements -------------------------------------- -PyBaMM comes with a DAE solver based on the IDA solver provided by the SUNDIALS library. -To use this solver, you must make sure that you have the necessary SUNDIALS components -installed on your system. +PyBaMM comes with a DAE solver based on the IDA solver provided by the SUNDIALS library. To use this solver, you must ensure you have the necessary SUNDIALS components installed on your system. +To install SUNDIALS, you will need to install the following components: -The IDA-based solver is currently unavailable on windows. -If you are running windows, you can simply skip this section and jump to :ref:`pybamm-install`. +.. tab:: GNU/Linux and MacOS -.. code:: bash + .. code:: bash + + # in the project root directory + nox -s pybamm-requires - # in the PyBaMM/ directory - nox -s pybamm-requires + This will download, compile and install the SuiteSparse and SUNDIALS libraries. + Both libraries are installed in ``PyBaMM/sundials_KLU_libs``. -This will download, compile and install the SuiteSparse and SUNDIALS libraries. -Both libraries are installed in ``~/.local``. + For users requiring more control over the installation process, the ``pybamm-requires`` session supports additional command-line arguments: -For users requiring more control over the installation process, the ``pybamm-requires`` session supports additional command-line arguments: + - ``--install-dir``: Specify a custom installation directory for SUNDIALS and SuiteSparse. -- ``--install-dir``: Specify a custom installation directory for SUNDIALS and SuiteSparse. + Example: + + .. code:: bash - Example: + nox -s pybamm-requires -- --install-dir [custom_directory_path] - .. code:: bash + After running this command, you need to export the environment variable ``INSTALL_DIR`` with the custom installation directory to link the libraries with the solver: + + .. code:: bash + + export INSTALL_DIR=[custom_directory_path] + + - ``--force``: Force the installation of SUNDIALS and SuiteSparse, even if they are already found in the specified directory. + + Example: + + .. code:: bash + + nox -s pybamm-requires -- --force + +.. tab:: Windows - nox -s pybamm-requires -- --install-dir [custom_directory_path] + VCPKG -- ``--force``: Force the installation of SUNDIALS and SuiteSparse, even if they are already found in the specified directory. + - VCPKG automatically installs the required libraries for you during the build process. To install VCPKG, follow the instructions in `Microsoft's official documentation `_. + - Make sure to add the location to ``Path`` and a new environment variable ``VCPKG_ROOT_DIR`` with the location to VCPKG. - Example: + C++ Compiler - .. code:: bash + - PyBaMM uses a recent version of Microsoft Visual C++ (MSVC), which you can get using `Build Tools for Visual Studio Code 2022 `_. + - Note that you won't need Visual Studio 2022 entirely; just ``Desktop development with C++`` will suffice. - nox -s pybamm-requires -- --force + CMake + + - ``CMake`` is required to install the SUNDIALS and other libraries for the ``IDAKLU`` solver. + - To install it, follow the link to the `official CMake downloads page `_. + - Download an installer based on your system's architecture, i.e. ``x32/x64``, and check ``Add CMake to the PATH environment variable`` during installation. Manual install of build time requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you'd rather do things yourself, +.. tab:: GNU/Linux and MacOS -1. Make sure you have CMake installed -2. Compile and install SuiteSparse (PyBaMM only requires the ``KLU`` component). -3. Compile and install SUNDIALS. -4. Clone the pybind11 repository in the ``PyBaMM/`` directory (make sure the directory is named ``pybind11``). + If you'd rather do things yourself, + 1. Make sure you have CMake installed + 2. Compile and install SuiteSparse (PyBaMM only requires the ``KLU`` component). + 3. Compile and install SUNDIALS. -PyBaMM ships with a Python script that automates points 2. and 3. You can run it with -.. code:: bash + PyBaMM ships with a Python script that automates points 2. and 3. You can run it with + + .. code:: bash - python scripts/install_KLU_Sundials.py + python scripts/install_KLU_Sundials.py -This script supports optional arguments for custom installations: + This script supports optional arguments for custom installations: -- ``--install-dir``: Specify a custom installation directory for SUNDIALS and SuiteSparse. - By default, they are installed in ``~/.local``. + - ``--install-dir``: Specify a custom installation directory for SUNDIALS and SuiteSparse. - Example: + By default, they are installed in ``PyBaMM/sundials_KLU_libs``. - .. code:: bash + Example: - python scripts/install_KLU_Sundials.py --install-dir [custom_directory_path] + .. code:: bash -- ``--force``: Force the installation of SUNDIALS and SuiteSparse, even if they are already found in the specified directory. + python scripts/install_KLU_Sundials.py --install-dir [custom_directory_path] - Example: + After running this command, you need to export the environment variable ``INSTALL_DIR`` with the custom installation directory to link the libraries with the solver: - .. code:: bash + .. code:: bash + + export INSTALL_DIR=[custom_directory_path] + + - ``--force``: Force the installation of SUNDIALS and SuiteSparse, even if they are already found in the specified directory. + + Example: + + .. code:: bash - python scripts/install_KLU_Sundials.py --force + python scripts/install_KLU_Sundials.py --force + +.. tab:: Windows + + There isn't a method to manually build SUNDIALS on Windows. However, if you've followed the instructions provided in the previous section (:ref:`install-build-time`), you should have successfully set up SUNDIALS on your Windows system. + + With SUNDIALS already set up, you can proceed directly to the next section. .. _pybamm-install: Installing PyBaMM ----------------- -You should now have everything ready to build and install PyBaMM successfully. +.. tab:: GNU/Linux and MacOS + + To obtain the PyBaMM source code, clone the GitHub repository + + .. code:: bash + + git clone https://github.com/pybamm-team/PyBaMM.git + + or download the source archive on the repository's homepage. + + You should now have everything ready to build and install PyBaMM successfully. + +.. tab:: Windows + + Open a Command Prompt and navigate to the folder where you want to install PyBaMM, + + 1. Obtain the PyBaMM source code by cloning the GitHub repository or downloading the source archive on the repository's homepage. + + .. code:: bash + + git clone https://github.com/pybamm-team/PyBaMM.git + + 2. PyBaMM requires setting a few environment variables to install the IDAKLU solver. To put them automatically, run the following ``.bat`` script using the following command from the project root directory. + + .. code:: + + .\scripts\windows_setup.bat + + The script sets the following environment variables with the following defaults. + + .. code-block:: bash + + PYBAMM_USE_VCPKG: ON + VCPKG_DEFAULT_TRIPLET: x64-windows-static-md + VCPKG_FEATURE_FLAGS: manifests,registries + + .. note:: + + Ensure you set the ``VCPKG_ROOT_DIR`` environment variable to the location where VCPKG is installed. + + You should now have everything ready to build and install PyBaMM successfully. Using ``Nox`` (recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To install PyBaMM, execute the following command + .. code:: bash - # in the PyBaMM/ directory + # in the project root directory nox -s dev .. note:: @@ -170,7 +294,7 @@ You can now activate the environment with .. tab:: Windows - .. code:: bash + .. code:: venv\Scripts\activate.bat @@ -179,25 +303,95 @@ and run the tests to check your installation. Manual install ~~~~~~~~~~~~~~ -From the ``PyBaMM/`` directory, you can install PyBaMM using +We recommend installing PyBaMM within a virtual environment to avoid altering any distribution of Python files. + +.. tab:: GNU/Linux and MacOS + + To create a virtual environment ``env`` within your current directory type: + + .. code:: bash + + virtualenv env + + You can then “activate” the environment using: + + .. code:: bash + + source env/bin/activate + +.. tab:: Windows + + You can install ``virtualenv`` by executing the following command: + + .. code:: bash + + python -m pip install virtualenv + + Create a virtual environment ``venv`` within the PyBaMM root directory: + + .. code:: bash + + python -m virtualenv venv + + You can then “activate” the environment using: + + .. code:: text + + venv\Scripts\activate.bat + +Now all the calls to pip described below will install PyBaMM and its +dependencies into the environment ``env``. When you are ready to exit +the environment and go back to your original system, just type: .. code:: bash - pip install . + deactivate + +From the ``PyBaMM/`` directory inside the virtual environment, you can install PyBaMM using + +.. code:: bash + + pip install . If you intend to contribute to the development of PyBaMM, it is convenient to install in "editable mode", along with all the optional dependencies and useful -tools for development and documentation: +tools for development and documentation. + +Due to the ``--no-build-isolation`` flag, you first need to install the build-time dependencies inside the virtual environment: + +.. tab:: GNU/Linux and MacOS + + .. code:: bash + + pip install scikit-build-core pybind11 casadi cmake + +.. tab:: Windows + + .. code:: bash + + pip install scikit-build-core pybind11 + +You can now install PyBaMM in "editable mode" using the following command: + +.. code:: bash + + pip install --no-build-isolation -e .[all,dev,docs] + +You can also install PyBaMM in "editable mode" with "partial rebuilds" for development using the following command: .. code:: bash - pip install -e .[all,dev,docs] + pip install --no-build-isolation --config-settings=editable.rebuild=true -e .[all,dev,docs] If you are using ``zsh`` or ``tcsh``, you would need to use different pattern matching: .. code:: bash - pip install -e '.[all,dev,docs]' + pip install --no-build-isolation --config-settings=editable.rebuild=true -e '.[all,dev,docs]' + +.. note:: + + The "partial rebuilds" feature is still experimental and may break. To learn more, check out `scikit-build-core's official documentation `_. Before you start contributing to PyBaMM, please read the `contributing guidelines `__. @@ -205,6 +399,14 @@ guidelines ` Running the tests ----------------- +.. tab:: GNU/Linux + + Make sure to install ``texlive-latex-extra`` to pass all tests. Otherwise, you can safely ignore the failed tests needing it. + +.. tab:: Windows and MacOS + + Make sure to install ``graphviz`` using the `Chocolatey `_ package manager (Windows) or using ``brew`` (MacOS) to pass all the tests. Otherwise, you can safely ignore the failed tests needing ``graphviz``. + Using Nox (recommended) ~~~~~~~~~~~~~~~~~~~~~~~ @@ -237,31 +439,32 @@ Finally, to run the unit and the integration suites sequentially, use nox -s tests -Using the test runner -~~~~~~~~~~~~~~~~~~~~~~ +Using ``pytest`` +~~~~~~~~~~~~~~~~ -You can run unit tests for PyBaMM using +You can run unit tests for PyBaMM inside the virtual environment using .. code:: bash - # in the PyBaMM/ directory - python run-tests.py --unit + pytest -m unit +You can run integration tests using -The above starts a sub-process using the current python interpreter (i.e. using your current -Python environment) and run the unit tests. This can take a few minutes. +.. code:: bash -You can also use the test runner to run the doctests: + pytest -m integration + +To run both unit and integration tests, use the following command: .. code:: bash - python run-tests.py --doctest + pytest -m tests -There is more to the PyBaMM test runner. To see a list of all options, type +You can also use ``pytest`` to test example notebooks. .. code:: bash - python run-tests.py --help + pytest --nbmake docs/source/examples/ How to build the PyBaMM documentation ------------------------------------- @@ -304,36 +507,86 @@ Here are some additional useful commands you can run with ``Nox``: Troubleshooting --------------- -**Problem:** I have made edits to source files in PyBaMM, but these are -not being used when I run my Python script. +.. tab:: GNU/Linux and MacOS + + **Problem:** I ran a ``nox``/python build command and encountered ``Could NOT find SUNDIALS (missing: SUNDIALS_INCLUDE_DIR SUNDIALS_LIBRARIES)`` error. + + **Solution:** This error occurs when the build system, ``scikit-build-core``, can not find the SUNDIALS libraries to build the ``IDAKLU`` solver. + + 1. Run the following command to ensure SUNDIALS libraries are installed: + + .. code:: bash -**Solution:** Make sure you have installed PyBaMM using the ``-e`` flag, -i.e. ``pip install -e .``. This sets the installed location of the -source files to your current directory. + nox -s pybamm-requires -- --force -**Problem:** Errors when solving model -``ValueError: Integrator name ida does not exist``, or -``ValueError: Integrator name cvode does not exist``. + 2. If you are using a custom directory for SUNDIALS, set the ``INSTALL_DIR`` environment variable to specify the path: -**Solution:** This could mean that you have not installed -``scikits.odes`` correctly, check the instructions given above and make -sure each command was successful. + .. code:: bash -One possibility is that you have not set your ``LD_LIBRARY_PATH`` to -point to the sundials library, type ``echo $LD_LIBRARY_PATH`` and make -sure one of the directories printed out corresponds to where the -SUNDIALS libraries are located. + export INSTALL_DIR=[custom_directory_path] -Another common reason is that you forget to install a BLAS library such -as OpenBLAS before installing SUNDIALS. Check the cmake output when you -configured SUNDIALS, it might say: + **Problem:** When installing SUNDIALS, I encountered ``CMake Error: The source "../CMakeLists.txt" does not match the source "../CMakeLists.txt" used to generate cache`` error. + + **Solution:** This error occurs when there is a delay between installing and downloading SUNDIALS libraries. + + 3. Remove the following directories from the PyBaMM directory if they exist: + + a. ``download_KLU_Sundials`` + b. ``sundials_KLU_libs`` + c. Any custom directory you have set for installation + + 4. Re-run the command to install SUNDIALS. + 5. If you are using a custom directory, make sure to set the ``INSTALL_DIR`` environment variable: + + .. code:: bash + + export INSTALL_DIR=[custom_directory_path] + + **Problem:** I have made edits to source files in PyBaMM, but these are + not being used when I run my Python script. + + **Solution:** Make sure you have installed PyBaMM using the ``-e`` flag, like so: + + .. code:: bash + + pip install --no-build-isolation -e . + + If you want to install with "partial rebuilds" enabled, use this command: + + .. code:: bash + + pip install --no-build-isolation --config-settings=editable.rebuild=true -e. + + Make sure you have the build-time dependencies installed beforehand. + + These commands set the installed location of the + source files to your current directory. + +.. tab:: Windows + + **Problem:** I ran a ``nox``/python build command and encountered ``Configuring incomplete, errors occurred!`` error. + + **Solution:** This can occur when the environment variables are improperly set in the terminal. + + 1. Make sure you've set environment variables before running any ``nox``/python build command. + 2. Try running the build command again in the same terminal. + + **Problem:** I have made edits to source files in PyBaMM, but these are + not being used when I run my Python script. + + **Solution:** Make sure you have installed PyBaMM using the ``-e`` flag, like so: + + .. code:: bash + + pip install --no-build-isolation -e . + + If you want to install with "partial rebuilds" enabled, use this command: + + .. code:: bash -:: + pip install --no-build-isolation --config-settings=editable.rebuild=true -e. - -- A library with BLAS API not found. Please specify library location. - -- LAPACK requires BLAS + Make sure you have the build-time dependencies installed beforehand. -If this is the case, on a Debian or Ubuntu system you can install -OpenBLAS using ``sudo apt-get install libopenblas-dev`` (or -``brew install openblas`` for Mac OS) and then re-install SUNDIALS using -the instructions above. + These commands set the installed location of the + source files to your current directory. diff --git a/docs/source/user_guide/installation/windows.rst b/docs/source/user_guide/installation/windows.rst index 44dc79a7d3..472a5cbd45 100644 --- a/docs/source/user_guide/installation/windows.rst +++ b/docs/source/user_guide/installation/windows.rst @@ -33,21 +33,21 @@ To install ``virtualenv``, type: python -m pip install virtualenv -To create a virtual environment ``env`` within your current directory +To create a virtual environment ``venv`` within your current directory type: .. code:: bash - python -m virtualenv env + python -m virtualenv venv You can then “activate” the environment using: .. code:: - env\Scripts\activate.bat + venv\Scripts\activate.bat Now all the calls to pip described below will install PyBaMM and its -dependencies into the environment ``env``. When you are ready to exit +dependencies into the environment ``venv``. When you are ready to exit the environment and go back to your original system, just type: .. code:: bash diff --git a/noxfile.py b/noxfile.py index 14bafcca47..c1fde49502 100644 --- a/noxfile.py +++ b/noxfile.py @@ -52,9 +52,21 @@ def set_iree_state(): return state +def get_build_dependencies(): + """ + Gets build-time dependencies from `pyproject.toml` specific to platform + """ + build_dependencies = nox.project.load_toml("pyproject.toml")["build-system"][ + "requires" + ] + return build_dependencies + + +project_dir = Path(__file__).parent.resolve() homedir = os.getenv("HOME") +libs_install_dir = project_dir / "sundials_KLU_libs" PYBAMM_ENV = { - "LD_LIBRARY_PATH": f"{homedir}/.local/lib", + "LD_LIBRARY_PATH": f"{homedir}/.local/lib:{libs_install_dir}", "PYTHONIOENCODING": "utf-8", "MPLBACKEND": "Agg", # Expression evaluators (...EXPR_CASADI cannot be fully disabled at this time) @@ -90,20 +102,6 @@ def run_pybamm_requires(session): if sys.platform != "win32": session.install("cmake", silent=False) session.run("python", "scripts/install_KLU_Sundials.py", *session.posargs) - if not os.path.exists("./pybind11"): - session.run( - "git", - "clone", - "--depth", - "1", - "--branch", - "v2.12.0", - "https://github.com/pybind/pybind11.git", - "pybind11/", - "-c", - "advice.detachedHead=false", - external=True, - ) if PYBAMM_ENV.get("PYBAMM_IDAKLU_EXPR_IREE") == "ON" and not os.path.exists( "./iree" ): @@ -135,15 +133,17 @@ def run_pybamm_requires(session): def run_coverage(session): """Run the coverage tests and generate an XML report.""" set_environment_variables(PYBAMM_ENV, session=session) - session.install("setuptools", silent=False) session.install("coverage", silent=False) # Using plugin here since coverage runs unit tests on linux with latest python version. if "CI" in os.environ: session.install("pytest-github-actions-annotate-failures") - session.install("-e", ".[all,dev,jax]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev,jax]", silent=False) if PYBAMM_ENV.get("PYBAMM_IDAKLU_EXPR_IREE") == "ON": # See comments in 'dev' session session.install( + "--no-build-isolation", "-e", ".[iree]", "--find-links", @@ -157,24 +157,24 @@ def run_coverage(session): def run_integration(session): """Run the integration tests.""" set_environment_variables(PYBAMM_ENV, session=session) - session.install("setuptools", silent=False) if ( "CI" in os.environ and sys.version_info[:2] == (3, 12) and sys.platform == "linux" ): session.install("pytest-github-actions-annotate-failures") - session.install("-e", ".[all,dev,jax]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev,jax]", silent=False) session.run("python", "-m", "pytest", "-m", "integration") @nox.session(name="doctests") def run_doctests(session): """Run the doctests and generate the output(s) in the docs/build/ directory.""" - # TODO: Temporary fix for Python 3.12 CI. - # See: https://bitbucket.org/pybtex-devs/pybtex/issues/169/ - session.install("setuptools", silent=False) - session.install("-e", ".[all,dev,docs]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev,docs]", silent=False) session.run( "python", "-m", @@ -188,11 +188,13 @@ def run_doctests(session): def run_unit(session): """Run the unit tests.""" set_environment_variables(PYBAMM_ENV, session=session) - session.install("setuptools", silent=False) - session.install("-e", ".[all,dev,jax]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev,jax]", silent=False) if PYBAMM_ENV.get("PYBAMM_IDAKLU_EXPR_IREE") == "ON": # See comments in 'dev' session session.install( + "--no-build-isolation", "-e", ".[iree]", "--find-links", @@ -206,8 +208,9 @@ def run_unit(session): def run_examples(session): """Run the examples tests for Jupyter notebooks.""" set_environment_variables(PYBAMM_ENV, session=session) - session.install("setuptools", silent=False) - session.install("-e", ".[all,dev,jax]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev,jax]", silent=False) notebooks_to_test = session.posargs if session.posargs else [] session.run( "pytest", "--nbmake", *notebooks_to_test, "docs/source/examples/", external=True @@ -218,11 +221,9 @@ def run_examples(session): def run_scripts(session): """Run the scripts tests for Python scripts.""" set_environment_variables(PYBAMM_ENV, session=session) - # Temporary fix for Python 3.12 CI. TODO: remove after - # https://bitbucket.org/pybtex-devs/pybtex/issues/169/replace-pkg_resources-with - # is fixed - session.install("setuptools", silent=False) - session.install("-e", ".[all,dev,jax]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev]", silent=False) session.run("python", "-m", "pytest", "-m", "scripts") @@ -232,9 +233,13 @@ def set_dev(session): set_environment_variables(PYBAMM_ENV, session=session) session.install("virtualenv", "cmake") session.run("virtualenv", os.fsdecode(VENV_DIR), silent=True) - python = os.fsdecode(VENV_DIR.joinpath("bin/python")) + if sys.platform == "win32": + python = os.fsdecode(VENV_DIR.joinpath("Scripts\\python")) + else: + python = os.fsdecode(VENV_DIR.joinpath("bin/python")) components = ["all", "dev", "jax"] args = [] + build_dependencies = get_build_dependencies() if PYBAMM_ENV.get("PYBAMM_IDAKLU_EXPR_IREE") == "ON": # Install IREE libraries for Jax-MLIR expression evaluation in the IDAKLU solver # (optional). IREE is currently pre-release and relies on nightly jaxlib builds. @@ -244,16 +249,16 @@ def set_dev(session): # - IREE compiler matches Jaxlib (use the matching nightly build) [pyproject.toml] components.append("iree") args = ["--find-links", PYBAMM_ENV.get("IREE_INDEX_URL")] - # Temporary fix for Python 3.12 CI. TODO: remove after - # https://bitbucket.org/pybtex-devs/pybtex/issues/169/replace-pkg_resources-with - # is fixed - session.run(python, "-m", "pip", "install", "setuptools", external=True) + session.run(python, "-m", "pip", "install", *build_dependencies) session.run( python, "-m", "pip", "install", - "-e", + "--no-build-isolation", + "--config-settings=editable.rebuild=true", + "-Cbuild_dir=build", + "-ve", ".[{}]".format(",".join(components)), *args, external=True, @@ -264,8 +269,9 @@ def set_dev(session): def run_tests(session): """Run the unit tests and integration tests sequentially.""" set_environment_variables(PYBAMM_ENV, session=session) - session.install("setuptools", silent=False) - session.install("-e", ".[all,dev,jax]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,dev,jax]", silent=False) specific_test_files = session.posargs if session.posargs else [] session.run( "python", "-m", "pytest", *specific_test_files, "-m", "unit or integration" @@ -276,10 +282,9 @@ def run_tests(session): def build_docs(session): """Build the documentation and load it in a browser tab, rebuilding on changes.""" envbindir = session.bin - # TODO: Temporary fix for Python 3.12 CI. - # See: https://bitbucket.org/pybtex-devs/pybtex/issues/169/ - session.install("setuptools", silent=False) - session.install("-e", ".[all,docs]", silent=False) + build_dependencies = get_build_dependencies() + session.install(*build_dependencies) + session.install("--no-build-isolation", "-e", ".[all,docs]", silent=False) session.chdir("docs") # Local development if session.interactive: diff --git a/pyproject.toml b/pyproject.toml index 1db7c927a3..9f6eeed9ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,14 @@ [build-system] requires = [ - "setuptools>=64", - "wheel", + "scikit-build-core>=0.10", # On Windows, use the CasADi vcpkg registry and CMake bundled from MSVC "casadi>=3.6.6; platform_system!='Windows'", # Note: the version of CasADi as a build-time dependency should be matched # across platforms, so updates to its minimum version here should be accompanied # by a version bump in https://github.com/pybamm-team/casadi-vcpkg-registry. - "cmake; platform_system!='Windows'", + "pybind11>=2.13.4", ] -build-backend = "setuptools.build_meta" +build-backend = "scikit_build_core.build" [project] name = "pybamm" @@ -154,15 +153,23 @@ ECM_Example = "pybamm.input.parameters.ecm.example_set:get_parameter_values" MSMR_Example = "pybamm.input.parameters.lithium_ion.MSMR_example_set:get_parameter_values" Chayambuka2022 = "pybamm.input.parameters.sodium_ion.Chayambuka2022:get_parameter_values" -[tool.setuptools] -include-package-data = true +[tool.scikit-build] +minimum-version = "build-system.requires" +build-dir = "build/{wheel_tag}" +build.verbose = true -# List of files to include as package data. These are mainly the parameter CSV files in -# the input/parameters/ subdirectories. Other files such as the CITATIONS file, relevant -# README.md files, and specific .txt files inside the pybamm/ directory are also included. -# These are specified to be included in the SDist through MANIFEST.in. -[tool.setuptools.package-data] -pybamm = [ +[tool.scikit-build.logging] +level = "INFO" + +[tool.scikit-build.wheel] +cmake = true + +[tool.scikit-build.cmake] +version = ">=3.13" +build-type = "Release" + +[tool.scikit-build.sdist] +include = [ "*.txt", "*.md", "*.csv", @@ -171,8 +178,11 @@ pybamm = [ "src/pybamm/plotting/mplstyle", ] -[tool.setuptools.packages.find] -where = ["src"] +[[tool.scikit-build.overrides]] +if.any.env.BUILD_IDAKLU = false +if.any.env.READTHEDOCS = true +wheel.cmake = false + [tool.ruff] extend-include = ["*.ipynb"] extend-exclude = ["__init__.py"] diff --git a/scripts/Dockerfile b/scripts/Dockerfile index e6fac122ff..05279b49d4 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -13,7 +13,8 @@ USER pybamm WORKDIR /home/pybamm/ -RUN git clone https://github.com/pybamm-team/PyBaMM.git +# TODO: Change clone to upstream before PR merge +RUN git clone --branch migrate-to-sbc https://github.com/cringeyburger/PyBaMM.git WORKDIR /home/pybamm/PyBaMM @@ -28,11 +29,9 @@ RUN uv venv $VIRTUAL_ENV RUN #!/bin/bash && source /home/pybamm/venv/bin/activate; ENV PATH="$VIRTUAL_ENV/bin:$PATH" -RUN uv pip install --upgrade setuptools wheel wget cmake - +RUN uv pip install --upgrade scikit-build-core wget cmake casadi pybind11 RUN python scripts/install_KLU_Sundials.py && \ -rm -rf pybind11 && \ -git clone https://github.com/pybind/pybind11.git && \ -uv pip install -e ".[all,dev,docs,jax]"; + uv pip install --no-build-isolation --config-settings=editable.rebuild=true -ve ".[all,dev,docs,jax]"; + ENTRYPOINT ["/bin/bash"] diff --git a/scripts/install_KLU_Sundials.py b/scripts/install_KLU_Sundials.py index 7a5466f467..445332f303 100755 --- a/scripts/install_KLU_Sundials.py +++ b/scripts/install_KLU_Sundials.py @@ -20,7 +20,7 @@ "7111b505c1207f6f4bd0be9740d0b2897e1146b845d73787df07901b4f5c1fb7" ) SUNDIALS_CHECKSUM = "4e0b998dff292a2617e179609b539b511eb80836f5faacf800e688a886288502" -DEFAULT_INSTALL_DIR = os.path.join(os.getenv("HOME"), ".local") +DEFAULT_INSTALL_DIR = "sundials_KLU_libs" def safe_remove_dir(path): @@ -285,7 +285,7 @@ def parallel_download(urls, download_dir): # Create download directory in PyBaMM dir pybamm_dir = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0] -download_dir = os.path.join(pybamm_dir, "install_KLU_Sundials") +download_dir = os.path.join(pybamm_dir, "download_KLU_Sundials") if not os.path.exists(download_dir): os.makedirs(download_dir) diff --git a/scripts/install_sundials.sh b/scripts/install_sundials.sh deleted file mode 100644 index 020166a188..0000000000 --- a/scripts/install_sundials.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -# This script installs both SuiteSparse -# (https://people.engr.tamu.edu/davis/suitesparse.html) and SUNDIALS -# (https://computing.llnl.gov/projects/sundials) from source. For each -# two library: -# - Archive downloaded and source code extracted in current working -# directory. -# - Library is built and installed. -# -# Usage: ./install_sundials.sh suitesparse_version sundials_version - -function prepend_python_bin_dir_to_PATH { - python_bin_dir_cmd="print(os.path.split(sys.executable)[0])" - python_bin_dir=$(python -c "import sys, os;$python_bin_dir_cmd") - export PATH=$python_bin_dir:$PATH -} - -function download { - ROOT_ADDR=$1 - FILENAME=$2 - - wget -q $ROOT_ADDR/$FILENAME -} - -function extract { - tar -xf $1 -} - -SUITESPARSE_VERSION=$1 -SUNDIALS_VERSION=$2 - -SUITESPARSE_ROOT_ADDR=https://github.com/DrTimothyAldenDavis/SuiteSparse/archive -SUNDIALS_ROOT_ADDR=https://github.com/LLNL/sundials/releases/download/v$SUNDIALS_VERSION - -SUITESPARSE_ARCHIVE_NAME=v$SUITESPARSE_VERSION.tar.gz -SUNDIALS_ARCHIVE_NAME=sundials-$SUNDIALS_VERSION.tar.gz - -yum -y update -yum -y install wget -download $SUITESPARSE_ROOT_ADDR $SUITESPARSE_ARCHIVE_NAME -download $SUNDIALS_ROOT_ADDR $SUNDIALS_ARCHIVE_NAME -extract $SUITESPARSE_ARCHIVE_NAME -extract $SUNDIALS_ARCHIVE_NAME - -# Build in parallel wherever possible -export MAKEFLAGS="-j$(nproc)" -export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) - -### Compile and install SUITESPARSE ### -# SuiteSparse is required to compile SUNDIALS's -# KLU solver. - -SUITESPARSE_DIR=SuiteSparse-$SUITESPARSE_VERSION -for dir in SuiteSparse_config AMD COLAMD BTF KLU -do - make -C $SUITESPARSE_DIR/$dir library - make -C $SUITESPARSE_DIR/$dir install INSTALL=/usr -done - -### Compile and install SUNDIALS ### - -# Building cmake requires cmake >= 3.5 -python -m pip install cmake -prepend_python_bin_dir_to_PATH - -# Building SUNDIALS requires a BLAS library -yum -y install openblas-devel - -mkdir -p build_sundials -cd build_sundials -KLU_INCLUDE_DIR=/usr/local/include -KLU_LIBRARY_DIR=/usr/local/lib -SUNDIALS_DIR=sundials-$SUNDIALS_VERSION -cmake -DENABLE_LAPACK=ON\ - -DSUNDIALS_INDEX_SIZE=32\ - -DEXAMPLES_ENABLE:BOOL=OFF\ - -DENABLE_KLU=ON\ - -DENABLE_OPENMP=ON\ - -DKLU_INCLUDE_DIR=$KLU_INCLUDE_DIR\ - -DKLU_LIBRARY_DIR=$KLU_LIBRARY_DIR\ - -DCMAKE_INSTALL_PREFIX=/usr\ - ../$SUNDIALS_DIR -make install diff --git a/scripts/windows_setup.bat b/scripts/windows_setup.bat new file mode 100644 index 0000000000..5193dac961 --- /dev/null +++ b/scripts/windows_setup.bat @@ -0,0 +1,10 @@ +@echo off + +echo Setting environment variables... + +setx PYBAMM_USE_VCPKG ON +setx VCPKG_DEFAULT_TRIPLET x64-windows-static-md +setx VCPKG_FEATURE_FLAGS manifests,registries + +echo Environment variables have been set. Please open a new Command Prompt to see the changes. +pause diff --git a/setup.py b/setup.py deleted file mode 100644 index 8a49bfd715..0000000000 --- a/setup.py +++ /dev/null @@ -1,348 +0,0 @@ -import os -import sys -import logging -import subprocess -from multiprocessing import cpu_count -from pathlib import Path -from platform import system -import wheel.bdist_wheel as orig - -from setuptools import setup, Extension -from setuptools.command.install import install -from setuptools.command.build_ext import build_ext - - -default_lib_dir = ( - "" if system() == "Windows" else os.path.join(os.getenv("HOME"), ".local") -) - -# ---------- set environment variables for vcpkg on Windows ---------------------------- - - -def set_vcpkg_environment_variables(): - if not os.getenv("VCPKG_ROOT_DIR"): - raise OSError("Environment variable 'VCPKG_ROOT_DIR' is undefined.") - if not os.getenv("VCPKG_DEFAULT_TRIPLET"): - raise OSError("Environment variable 'VCPKG_DEFAULT_TRIPLET' is undefined.") - if not os.getenv("VCPKG_FEATURE_FLAGS"): - raise OSError("Environment variable 'VCPKG_FEATURE_FLAGS' is undefined.") - return ( - os.getenv("VCPKG_ROOT_DIR"), - os.getenv("VCPKG_DEFAULT_TRIPLET"), - os.getenv("VCPKG_FEATURE_FLAGS"), - ) - - -# ---------- CMakeBuild class (custom build_ext for IDAKLU target) --------------------- - - -class CMakeBuild(build_ext): - user_options = [ - *build_ext.user_options, - ("suitesparse-root=", None, "suitesparse source location"), - ("sundials-root=", None, "sundials source location"), - ] - - def initialize_options(self): - build_ext.initialize_options(self) - self.suitesparse_root = None - self.sundials_root = None - - def finalize_options(self): - build_ext.finalize_options(self) - # Determine the calling command to get the - # undefined options from. - # If build_ext was called directly then this - # doesn't matter. - try: - self.get_finalized_command("install", create=0) - calling_cmd = "install" - except AttributeError: - calling_cmd = "bdist_wheel" - self.set_undefined_options( - calling_cmd, - ("suitesparse_root", "suitesparse_root"), - ("sundials_root", "sundials_root"), - ) - if not self.suitesparse_root: - self.suitesparse_root = os.path.join(default_lib_dir) - if not self.sundials_root: - self.sundials_root = os.path.join(default_lib_dir) - - def get_build_directory(self): - # setuptools outputs object files in directory self.build_temp - # (typically build/temp.*). This is our CMake build directory. - # On Windows, setuptools is too smart and appends "Release" or - # "Debug" to self.build_temp. So in this case we want the - # build directory to be the parent directory. - if system() == "Windows": - return Path(self.build_temp).parents[0] - return self.build_temp - - def run(self): - if not self.extensions: - return - - # Build in parallel wherever possible - os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = str(cpu_count()) - - if system() == "Windows": - use_python_casadi = False - else: - use_python_casadi = True - - build_type = os.getenv("PYBAMM_CPP_BUILD_TYPE", "RELEASE") - idaklu_expr_casadi = os.getenv("PYBAMM_IDAKLU_EXPR_CASADI", "ON") - idaklu_expr_iree = os.getenv("PYBAMM_IDAKLU_EXPR_IREE", "OFF") - cmake_args = [ - f"-DCMAKE_BUILD_TYPE={build_type}", - f"-DPYTHON_EXECUTABLE={sys.executable}", - "-DUSE_PYTHON_CASADI={}".format("TRUE" if use_python_casadi else "FALSE"), - f"-DPYBAMM_IDAKLU_EXPR_CASADI={idaklu_expr_casadi}", - f"-DPYBAMM_IDAKLU_EXPR_IREE={idaklu_expr_iree}", - ] - if self.suitesparse_root: - cmake_args.append( - f"-DSuiteSparse_ROOT={os.path.abspath(self.suitesparse_root)}" - ) - if self.sundials_root: - cmake_args.append(f"-DSUNDIALS_ROOT={os.path.abspath(self.sundials_root)}") - - build_dir = self.get_build_directory() - if not os.path.exists(build_dir): - os.makedirs(build_dir) - - # The CMakeError.log file is generated by cmake is the configure step - # encounters error. In the following the existence of this file is used - # to determine whether or not the cmake configure step went smoothly. - # So must make sure this file does not remain from a previous failed build. - if os.path.isfile(os.path.join(build_dir, "CMakeError.log")): - os.remove(os.path.join(build_dir, "CMakeError.log")) - - # ---------- configuration for vcpkg on Windows ---------------------------------------- - - build_env = os.environ - if os.getenv("PYBAMM_USE_VCPKG"): - ( - vcpkg_root_dir, - vcpkg_default_triplet, - vcpkg_feature_flags, - ) = set_vcpkg_environment_variables() - build_env["vcpkg_root_dir"] = vcpkg_root_dir - build_env["vcpkg_default_triplet"] = vcpkg_default_triplet - build_env["vcpkg_feature_flags"] = vcpkg_feature_flags - - # ---------- Run CMake and build IDAKLU module ----------------------------------------- - - cmake_list_dir = os.path.abspath(os.path.dirname(__file__)) - print("-" * 10, "Running CMake for IDAKLU solver", "-" * 40) - subprocess.run( - ["cmake", cmake_list_dir, *cmake_args], - cwd=build_dir, - env=build_env, - check=True, - ) - - if os.path.isfile(os.path.join(build_dir, "CMakeError.log")): - msg = ( - "cmake configuration steps encountered errors, and the IDAKLU module" - " could not be built. Make sure dependencies are correctly " - "installed. See " - "https://docs.pybamm.org/en/latest/source/user_guide/installation/install-from-source.html" - ) - raise RuntimeError(msg) - else: - print("-" * 10, "Building IDAKLU module", "-" * 40) - subprocess.run( - ["cmake", "--build", ".", "--config", "Release"], - cwd=build_dir, - env=build_env, - check=True, - ) - - # Move from build temp to final position - for ext in self.extensions: - self.move_output(ext) - - def move_output(self, ext): - # Copy built module to dist/ directory - build_temp = Path(self.build_temp).resolve() - # Get destination location - # self.get_ext_fullpath(ext.name) --> - # build/lib.linux-x86_64-3.5/idaklu.cpython-37m-x86_64-linux-gnu.so - # using resolve() with python < 3.6 will result in a FileNotFoundError - # since the location does not yet exists. - dest_path = Path(self.get_ext_fullpath(ext.name)).resolve() - source_path = build_temp / os.path.basename(self.get_ext_filename(ext.name)) - dest_directory = dest_path.parents[0] - dest_directory.mkdir(parents=True, exist_ok=True) - self.copy_file(source_path, dest_path) - - -# ---------- end of CMake steps -------------------------------------------------------- - - -# ---------- configure setup logger ---------------------------------------------------- - - -log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" -logger = logging.getLogger("PyBaMM setup") - -# To override the default severity of logging -logger.setLevel("INFO") - -# Use FileHandler() to log to a file -logfile = os.path.join(os.path.dirname(os.path.abspath(__file__)), "setup.log") -file_handler = logging.FileHandler(logfile) -formatter = logging.Formatter(log_format) -file_handler.setFormatter(formatter) - -# Add the file handler -logger.addHandler(file_handler) -logger.info("Starting PyBaMM setup") - - -class CustomInstall(install): - """A custom install command to add 2 build options""" - - user_options = [ - *install.user_options, - ("suitesparse-root=", None, "suitesparse source location"), - ("sundials-root=", None, "sundials source location"), - ] - - def initialize_options(self): - install.initialize_options(self) - self.suitesparse_root = None - self.sundials_root = None - - def finalize_options(self): - install.finalize_options(self) - if not self.suitesparse_root: - self.suitesparse_root = default_lib_dir - if not self.sundials_root: - self.sundials_root = default_lib_dir - - def run(self): - install.run(self) - - -# ---------- Custom class for building wheels ------------------------------------------ - - -class bdist_wheel(orig.bdist_wheel): - """A custom install command to add 2 build options""" - - user_options = [ - *orig.bdist_wheel.user_options, - ("suitesparse-root=", None, "suitesparse source location"), - ("sundials-root=", None, "sundials source location"), - ] - - def initialize_options(self): - orig.bdist_wheel.initialize_options(self) - self.suitesparse_root = None - self.sundials_root = None - - def finalize_options(self): - orig.bdist_wheel.finalize_options(self) - if not self.suitesparse_root: - self.suitesparse_root = default_lib_dir - if not self.sundials_root: - self.sundials_root = default_lib_dir - - def run(self): - orig.bdist_wheel.run(self) - - -def compile_KLU(): - # Return whether or not the KLU extension should be compiled. - # Return True if: - # - Not running on Windows AND - # - CMake is found AND - # - The pybind11/ directory is found in the PyBaMM project directory - CMakeFound = True - PyBind11Found = True - windows = (not system()) or system() == "Windows" - - msg = "Running on Windows" if windows else "Not running on windows" - logger.info(msg) - - try: - subprocess.run(["cmake", "--version"]) - logger.info("Found CMake.") - except OSError: - CMakeFound = False - logger.info("Could not find CMake. Skipping compilation of KLU module.") - - pybamm_project_dir = os.path.dirname(os.path.abspath(__file__)) - pybind11_dir = os.path.join(pybamm_project_dir, "pybind11") - try: - open(os.path.join(pybind11_dir, "tools", "pybind11Tools.cmake")) - logger.info(f"Found pybind11 directory ({pybind11_dir})") - except FileNotFoundError: - PyBind11Found = False - msg = ( - f"Could not find PyBind11 directory ({pybind11_dir})." - " Skipping compilation of KLU module." - ) - logger.info(msg) - - return CMakeFound and PyBind11Found - - -idaklu_ext = Extension( - name="pybamm.solvers.idaklu", - # The sources list should mirror the list in CMakeLists.txt - sources=[ - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Expressions.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Base/Expression.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Base/ExpressionSet.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Base/ExpressionTypes.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Base/ExpressionSparsity.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Casadi/CasadiFunctions.cpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/Casadi/CasadiFunctions.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/IREEBaseFunction.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/IREEFunction.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/IREEFunctions.cpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/IREEFunctions.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/iree_jit.cpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/iree_jit.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/ModuleParser.cpp", - "src/pybamm/solvers/c_solvers/idaklu/Expressions/IREE/ModuleParser.hpp", - "src/pybamm/solvers/c_solvers/idaklu/idaklu_solver.hpp", - "src/pybamm/solvers/c_solvers/idaklu/IDAKLUSolver.cpp", - "src/pybamm/solvers/c_solvers/idaklu/IDAKLUSolver.hpp", - "src/pybamm/solvers/c_solvers/idaklu/IDAKLUSolverOpenMP.inl", - "src/pybamm/solvers/c_solvers/idaklu/IDAKLUSolverOpenMP.hpp", - "src/pybamm/solvers/c_solvers/idaklu/IDAKLUSolverOpenMP_solvers.cpp", - "src/pybamm/solvers/c_solvers/idaklu/IDAKLUSolverOpenMP_solvers.hpp", - "src/pybamm/solvers/c_solvers/idaklu/sundials_functions.inl", - "src/pybamm/solvers/c_solvers/idaklu/sundials_functions.hpp", - "src/pybamm/solvers/c_solvers/idaklu/IdakluJax.cpp", - "src/pybamm/solvers/c_solvers/idaklu/IdakluJax.hpp", - "src/pybamm/solvers/c_solvers/idaklu/common.hpp", - "src/pybamm/solvers/c_solvers/idaklu/common.cpp", - "src/pybamm/solvers/c_solvers/idaklu/Solution.cpp", - "src/pybamm/solvers/c_solvers/idaklu/Solution.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Options.hpp", - "src/pybamm/solvers/c_solvers/idaklu/Options.cpp", - "src/pybamm/solvers/c_solvers/idaklu/observe.hpp", - "src/pybamm/solvers/c_solvers/idaklu/observe.cpp", - "src/pybamm/solvers/c_solvers/idaklu.cpp", - ], -) -ext_modules = [idaklu_ext] if compile_KLU() else [] - -# Project metadata was moved to pyproject.toml (which is read by pip). However, custom -# build commands and setuptools extension modules are still defined here. -setup( - # silence "Package would be ignored" warnings - include_package_data=True, - ext_modules=ext_modules, - cmdclass={ - "build_ext": CMakeBuild, - "bdist_wheel": bdist_wheel, - "install": CustomInstall, - }, -) diff --git a/tests/unit/test_expression_tree/test_operations/test_latexify.py b/tests/unit/test_expression_tree/test_operations/test_latexify.py index 66cd30d399..ada4aa7c84 100644 --- a/tests/unit/test_expression_tree/test_operations/test_latexify.py +++ b/tests/unit/test_expression_tree/test_operations/test_latexify.py @@ -6,10 +6,20 @@ import os import platform import uuid +import subprocess # nosec import pybamm +def is_latex_installed(): + try: + # Try to run the LaTeX version command + subprocess.run("latex --version", check=True, shell=True, capture_output=True) # nosec + return True + except (subprocess.CalledProcessError, FileNotFoundError): + return False + + class TestLatexify: def test_latexify(self): model_dfn = pybamm.lithium_ion.DFN() @@ -83,6 +93,7 @@ def test_latexify_other_variables(self): @pytest.mark.skipif( platform.system() in ["Windows", "Darwin"], reason="Only run for Linux" ) + @pytest.mark.skipif(not is_latex_installed(), reason="LaTeX is not installed") def test_sympy_preview(self): # Test sympy preview model_spme = pybamm.lithium_ion.SPMe() diff --git a/tests/unit/test_solvers/test_idaklu_jax.py b/tests/unit/test_solvers/test_idaklu_jax.py index 53abb94c83..8b8e0e61d5 100644 --- a/tests/unit/test_solvers/test_idaklu_jax.py +++ b/tests/unit/test_solvers/test_idaklu_jax.py @@ -6,6 +6,8 @@ import pybamm import numpy as np +import unittest +import sys testcase = [] if pybamm.has_idaklu() and pybamm.has_jax(): @@ -99,7 +101,11 @@ def test_instantiate_fails(self): not pybamm.has_idaklu() or not pybamm.has_jax(), reason="IDAKLU Solver and/or JAX are not available", ) -class TestIDAKLUJax: +@pytest.mark.skipif( + sys.platform.startswith("win"), + reason="Problem with tests crashing workers on Windows", +) +class TestIDAKLUJax(unittest.TestCase): # Initialisation tests def test_initialise_twice(self):