From f3252f6a3f57539d949d6c5b3c378497bf1e6d85 Mon Sep 17 00:00:00 2001 From: Jack Betteridge <43041811+JDBetteridge@users.noreply.github.com> Date: Sat, 7 Dec 2024 16:14:32 +0000 Subject: [PATCH] Build system added to CI --- .github/workflows/ngsPETSc.yml | 8 +- .github/workflows/release.yml | 177 +++++++++++++++++++++++++++++++++ Makefile | 26 ++--- README.md | 27 +++-- docs/release.md | 54 ++++++++++ ngsPETSc/eps.py | 4 +- ngsPETSc/plex.py | 4 +- pyproject.toml | 60 ++++++----- setup.py | 28 ------ 9 files changed, 295 insertions(+), 93 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 docs/release.md delete mode 100644 setup.py diff --git a/.github/workflows/ngsPETSc.yml b/.github/workflows/ngsPETSc.yml index 2866456..8876f46 100644 --- a/.github/workflows/ngsPETSc.yml +++ b/.github/workflows/ngsPETSc.yml @@ -83,7 +83,7 @@ jobs: pip install netgen-mesher --break-system-packages export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python3.10/site-packages echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV - NGSPETSC_NO_INSTALL_REQUIRED=ON pip install -e . --break-system-packages + pip install -e . --break-system-packages - name: Run test suite in serial run: | @@ -109,9 +109,8 @@ jobs: - name: Install Netgen and ngsPETSc run: | . /home/firedrake/firedrake/bin/activate - pip install netgen-mesher pip install xdist pytest-timeout ipympl - NGSPETSC_NO_INSTALL_REQUIRED=ON pip install . + pip install . - name: Run part of the Firedrake test suite run: | @@ -135,9 +134,8 @@ jobs: - name: Install Netgen and ngsPETSc run: | . /home/firedrake/firedrake/bin/activate - pip install netgen-mesher pip install xdist pytest-timeout ipympl - NGSPETSC_NO_INSTALL_REQUIRED=ON pip install . + pip install . - name: Run part of the Firedrake test suite run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..446c301 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,177 @@ +name: release + +# Shamelessly taken from: https://github.com/ArjanCodes/examples/blob/main/2024/publish_pypi/release.yaml + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+a[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+b[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+dev[0-9]+" + +env: + PACKAGE_NAME: "ngsPETSc" + OWNER: "uzerbinati" + +jobs: + details: + runs-on: ubuntu-latest + outputs: + new_version: ${{ steps.release.outputs.new_version }} + suffix: ${{ steps.release.outputs.suffix }} + tag_name: ${{ steps.release.outputs.tag_name }} + steps: + - uses: actions/checkout@v4 + + - name: Extract tag and Details + id: release + run: | + if [ "${{ github.ref_type }}" = "tag" ]; then + TAG_NAME=${GITHUB_REF#refs/tags/} + NEW_VERSION=$(echo $TAG_NAME | awk -F'-' '{print $1}') + SUFFIX=$(echo $TAG_NAME | grep -oP '([a-u,w-z]|dev)+[0-9]+' || echo "") + echo "TAG_NAME=$TAG_NAME" + echo "new_version=${NEW_VERSION:1}" >> "$GITHUB_OUTPUT" + echo "suffix=$SUFFIX" >> "$GITHUB_OUTPUT" + echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" + echo "Version is ${NEW_VERSION:1}" + echo "Suffix is $SUFFIX" + echo "Tag name is $TAG_NAME" + else + echo "No tag found" + exit 1 + fi + + check_pypi: + needs: details + runs-on: ubuntu-latest + steps: + - name: Fetch information from PyPI + run: | + response=$(curl -s https://pypi.org/pypi/${{ env.PACKAGE_NAME }}/json || echo "{}") + latest_previous_version=$(echo $response | jq --raw-output "select(.releases != null) | .releases | keys_unsorted | last") + if [ -z "$latest_previous_version" ]; then + echo "Package not found on PyPI." + latest_previous_version="0.0.0" + fi + echo "Latest version on PyPI: $latest_previous_version" + echo "latest_previous_version=$latest_previous_version" >> $GITHUB_ENV + + - name: Compare versions and exit if not newer + run: | + NEW_VERSION=${{ needs.details.outputs.new_version }} + LATEST_VERSION=$latest_previous_version + if [ "$(printf '%s\n' "$LATEST_VERSION" "$NEW_VERSION" | sort -rV | head -n 1)" != "$NEW_VERSION" ] || [ "$NEW_VERSION" == "$LATEST_VERSION" ]; then + echo "The new version $NEW_VERSION is not greater than the latest version $LATEST_VERSION on PyPI." + exit 1 + else + echo "The new version $NEW_VERSION is greater than the latest version $LATEST_VERSION on PyPI." + fi + + setup_and_build: + needs: [details, check_pypi] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Setup MPI + uses: mpi4py/setup-mpi@v1 + with: + mpi: "mpich" + + - name: Build and install PETSc + run: | + pip install numpy setuptools + cd .. + git clone https://gitlab.com/petsc/petsc.git petsc + cd petsc + git checkout -b stefanozampini/fix-petsc4py-sdist + ./configure \ + --with-c2html=0 \ + --with-debugging=0 \ + --with-fortran-bindings=0 \ + --with-shared-libraries=1 \ + --with-petsc4py + make + + - name: Install Poetry + run: | + curl -sSL https://install.python-poetry.org | python3 - + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Set project version with Poetry + run: | + poetry version ${{ needs.details.outputs.new_version }} + + - name: Install dependencies + run: | + export PETSC_DIR=/home/runner/work/ngsPETSc/petsc + export PETSC_ARCH=arch-linux-c-opt + # Hopefully this song and dance goes away after petsc4py release 3.22.2 + poetry remove petsc4py + poetry install --sync --no-interaction + poetry add ../petsc/src/binding/petsc4py + sed -i -E "s/petsc4py = (\{.+\})/petsc4py = \"^3.22.1\"/" pyproject.toml + # Replace when fixed with: + # poetry install --sync --no-interaction + + - name: Build source and wheel distribution + run: | + poetry build + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + pypi_publish: + name: Upload release to PyPI + needs: [setup_and_build, details] + runs-on: ubuntu-latest + environment: + name: release + permissions: + id-token: write + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + github_release: + name: Create GitHub Release + needs: [setup_and_build, details] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Create GitHub Release + id: create_release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create ${{ needs.details.outputs.tag_name }} dist/* --title ${{ needs.details.outputs.tag_name }} --generate-notes + diff --git a/Makefile b/Makefile index c008fab..1fb4f33 100644 --- a/Makefile +++ b/Makefile @@ -1,35 +1,25 @@ MPI_EXEC = ${PETSC_DIR}/${PETSC_ARCH}/bin/mpiexec GITHUB_ACTIONS_FORMATTING=0 + ifeq ($(GITHUB_ACTIONS_FORMATTING), 1) PYLINT_FORMAT=--msg-template='::error file={path},line={line},col={column},title={msg_id}::{path}:{line}:{column}: {msg_id} {msg}' else PYLINT_FORMAT= endif + lint: pylint ${PYLINT_FORMAT} --disable=C0412,C0103,C0415,C0321,C3001,E0401,E1101,E0611,R1728,R1736,R0401,R0801,R0902,R1702,R0913,R0914,R0903,R0205,R0912,R0915,R0917,I1101,W0102,W0201,W0212,W0406,C0209 --variable-naming-style=camelCase --class-naming-style=PascalCase --argument-naming-style=camelCase --attr-naming-style=camelCase ngsPETSc pylint ${PYLINT_FORMAT} --disable=C0412,C0103,C0415,C0321,C3001,E0401,E1101,E0611,R1728,R1736,R0401,R0801,R0902,R1702,R0913,R0914,R0903,R0205,R0912,R0915,R0917,I1101,W0102,W0201,W0212,W0406,C0209 --variable-naming-style=camelCase --class-naming-style=PascalCase --argument-naming-style=camelCase --attr-naming-style=camelCase ngsPETSc/utils + lint_test: pylint ${PYLINT_FORMAT} --disable=C0412,C0103,C0415,C0321,E0401,E1101,E0611,R1728,R1736,R0401,R0914,R0801,R0902,R1702,R0913,R0903,R0205,R0912,R0915,I1101,W0201,C0209 --variable-naming-style=camelCase --class-naming-style=PascalCase --argument-naming-style=camelCase --attr-naming-style=camelCase tests + test: - pytest tests/test_env.py - pytest tests/test_vec.py - pytest tests/test_mat.py - pytest tests/test_plex.py - pytest tests/test_ksp.py - pytest tests/test_pc.py - pytest tests/test_eps.py - pytest tests/test_snes.py - pytest tests/test_fenicsx.py + pytest tests + test_mpi: - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_env.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_vec.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_mat.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_plex.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_ksp.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_pc.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_eps.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_snes.py - $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests/test_fenicsx.py + $(MPI_EXEC) --allow-run-as-root -n 2 pytest --with-mpi tests + doc: rm docs/src/notebooks/*.rst jupyter nbconvert --to rst docs/src/notebooks/*.ipynb diff --git a/README.md b/README.md index 5d652fd..7808779 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,29 @@ # ngsPETSc - -ngsPETSc is an interface between PETSc and NGSolve/NETGEN that enables the use of NETGEN meshes and geometries in PETSc-based solvers while providing NGSolve users access to the wide array of linear, nonlinear solvers, and time-steppers available in PETSc. - [![ngsPETSc](https://github.com/UZerbinati/ngsPETSc/actions/workflows/ngsPETSc.yml/badge.svg)](https://github.com/UZerbinati/ngsPETSc/actions/workflows/ngsPETSc.yml) [![Documentation Status](https://readthedocs.org/projects/ngspetsc/badge/?version=latest)](https://ngspetsc.readthedocs.io/en/latest/?badge=latest) +ngsPETSc is an interface between PETSc and NGSolve/NETGEN that enables the use of NETGEN meshes and geometries in PETSc-based solvers while providing NGSolve users access to the wide array of linear, nonlinear solvers, and time-steppers available in PETSc. ## Installation -If you already have NGSolve (with MPI support) and PETSc installed, you can install ngsPETSc via pip: +ngsPETSc is available on [PyPI](https://pypi.org/project/ngsPETSc/). +If you have PETSc installed be sure to set the `PETSC_DIR` and `PETSC_ARCH` environment variables to the required values. +You can install by running: ```bash - - git clone https://github.com/UZerbinati/ngsPETSc.git - cd ngsPETSc - pip install . +pip install ngsPETSc ``` -Alternatively, you can also build PETSc, SLEPc, and NGSolve from source following the instructions in the [documentation](https://ngspetsc.readthedocs.io/en/latest/install.html). ## Getting started - To get started with ngsPETSc, check out the [documentation](https://ngspetsc.readthedocs.io/en/latest/). + +## Development +If you already have NGSolve (with MPI support) and PETSc installed, you can install ngsPETSc via pip: +```bash +git clone https://github.com/UZerbinati/ngsPETSc.git +pip install ./ngsPETSc +``` +Alternatively, you can also build PETSc, SLEPc, and NGSolve from source following the instructions in the [documentation](https://ngspetsc.readthedocs.io/en/latest/install.html). + +### Testing To test the installation, you can run the tests in the `tests` folder, via the Makefile in the root directory of the repository: ```bash - make test +make test ``` diff --git a/docs/release.md b/docs/release.md new file mode 100644 index 0000000..71a670e --- /dev/null +++ b/docs/release.md @@ -0,0 +1,54 @@ +# Poetry notes for Umberto + +## Poetry +Remember that poetry cannot be installed in the same environment as the project. +Create a virtual environment somewhere permanent and you can use a bash alias to drive poetry: + +```bash +python -m venv ~/.poetry +source ~/.poetry/bin/activate +pip install poetry + +# check location of executable with +which poetry + +# create alias (could be added to ~/.bash_profile) +alias poetry="$HOME/.poetry/bin/poetry" +``` + +When carrying out any of the below tasks create a new empty virtual environment! +```bash +python -m venv temp +source temp/bin/activate +``` + +## Add a dependency +[Docs](https://python-poetry.org/docs/cli/#add) +```bash +poetry add numpy@^2 +``` + +## Build ngsPETSc +[Docs](https://python-poetry.org/docs/cli/#build) +```bash +poetry build +``` + +## Create a release +Follow these steps to create a release: +```bash +# For a bug fix: +poetry version patch +# OR for a minor version bump: +poetry version minor +# OR for a major release: +poetry version minor + +git add pyproject.toml +git commit -m "Release v$(poetry version)" + +git tag -a -m "Release v$(poetry version)" v$(poetry version) + +git push +git push origin v$(poetry version) +``` diff --git a/ngsPETSc/eps.py b/ngsPETSc/eps.py index 6bd6a3b..0e1c724 100644 --- a/ngsPETSc/eps.py +++ b/ngsPETSc/eps.py @@ -10,8 +10,6 @@ warnings.warn("Import Warning: it was not possible to import SLEPc") SLEPc = None -from mpi4py import MPI - from ngsolve import GridFunction from ngsPETSc import Matrix, VectorMapping @@ -41,7 +39,7 @@ class EigenSolver(): if SLEPc is not None: def __init__(self, pencil, fes, nev, ncv=SLEPc.DECIDE, optionsPrefix=None, solverParameters=None): - self.comm = MPI.COMM_WORLD + self.comm = PETSc.COMM_WORLD.tompi4py() if not isinstance(pencil, tuple): pencil=tuple([pencil]) self.penLength = len(pencil) self.fes = fes diff --git a/ngsPETSc/plex.py b/ngsPETSc/plex.py index fd50d06..62afc99 100644 --- a/ngsPETSc/plex.py +++ b/ngsPETSc/plex.py @@ -17,8 +17,6 @@ class comp: "dummy class" Mesh = type(None) -from mpi4py import MPI - FACE_SETS_LABEL = "Face Sets" CELL_SETS_LABEL = "Cell Sets" EDGE_SETS_LABEL = "Edge Sets" @@ -33,7 +31,7 @@ class MeshMapping: ''' - def __init__(self, mesh=None, comm=MPI.COMM_WORLD, name="Default"): + def __init__(self, mesh=None, comm=PETSc.COMM_WORLD.tompi4py(), name="Default"): self.name = name self.comm = comm if isinstance(mesh,(ngs.comp.Mesh,ngm.Mesh)): diff --git a/pyproject.toml b/pyproject.toml index 269d8c6..11f0f55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,24 +1,23 @@ -[project] +[tool.poetry] name = "ngsPETSc" -version = "0.0.5" -description = "NGSolve/Netgen interface to PETSc." -readme = "README.md" +version = "0.0.8" +description = "NGSolve/Netgen interface to PETSc" authors = [ - {name = "Umberto Zerbinati", email = "umberto.zerbinati@maths.ox.ac.uk"}, - {name = "Patrick E. Farrell", email = "patrick.farrell@maths.ox.ac.uk"}, - {name = "Stefano Zampini", email = "stefano.zampini@kaust.edu.sa"}, - {name = "Jack Betteridge", email = "J.Betteridge@imperial.ac.uk"}, + "Umberto Zerbinati ", + "Patrick E. Farrell ", + "Stefano Zampini ", + "Jack Betteridge ", ] maintainers = [ - {name = "Umberto Zerbinati", email = "umberto.zerbinati@maths.ox.ac.uk"}, + "Umberto Zerbinati ", ] -license = {file = "LICENSE.txt"} -dependencies = [ - "mpi4py", - "numpy", - "scipy", +readme = "README.md" +license = "MIT" +packages = [ + {include = "ngsPETSc"} ] -requires-python = ">=3.8" +documentation = "https://ngspetsc.readthedocs.io/en/latest/" +repository = "https://github.com/NGSolve/ngsPETSc" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", @@ -26,16 +25,27 @@ classifiers = [ "Development Status :: 3 - Alpha", ] -[project.urls] -Documentation = "https://ngspetsc.readthedocs.io/en/latest/" -Repository = "https://github.com/NGSolve/ngsPETSc" +[tool.poetry.dependencies] +python = "^3.9" +netgen-mesher = "^6.2" +netgen-occt = "^7.8" +petsc4py = "^3.22.1" +numpy = "^2" +scipy = "^1" -[project.optional-dependencies] -dev = [ - "pytest", - "pylint", -] +[tool.poetry.group.dev.dependencies] +pytest = "^8.3" +pylint = "^3.3" + +[tool.poetry.group.ngsolve.dependencies] +ngsolve = "^6.2" +mpi4py = "^4" [build-system] -requires = ['setuptools>=42'] -build-backend = 'setuptools.build_meta' +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[project.optional-dependencies] +firedrake = [ + "firedrake", +] diff --git a/setup.py b/setup.py deleted file mode 100644 index 5ef6ff9..0000000 --- a/setup.py +++ /dev/null @@ -1,28 +0,0 @@ -import setuptools, os - -if 'NGSPETSC_NO_INSTALL_REQUIRED' in os.environ: - install_requires = [] -elif 'NGS_FROM_SOURCE' in os.environ: - install_requires = [ - 'petsc4py', - 'mpi4py', - 'numpy', - 'scipy', - 'pytest', #For testing - 'pylint', #For formatting - ] -else: - install_requires=[ - 'netgen-mesher', - 'ngsolve', - 'petsc4py', - 'mpi4py', - 'scipy', - 'pytest', #For testing - 'pylint', #For formatting - ] - -setuptools.setup( - install_requires=install_requires, - packages=["ngsPETSc", "ngsPETSc.utils", "ngsPETSc.utils.firedrake", "ngsPETSc.utils.ngs"] -)