diff --git a/.github/workflows/build-env.yml b/.github/workflows/build-env.yml new file mode 100644 index 00000000..273d90f0 --- /dev/null +++ b/.github/workflows/build-env.yml @@ -0,0 +1,28 @@ +name: Update build environment + +on: + workflow_dispatch: # Trigger by hand from the UI + schedule: + - cron: '0 0 * * 0' # Schedule it every Sunday + +jobs: + build-spack-pyprecice-deps: + name: Builds the baseimage for spack providing dependencies + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + - name: Set up Docker + uses: docker/setup-buildx-action@v1 + - name: Login to registry + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + push: true + context: . + file: spack/ci-spack-pyprecice-deps-1804.dockerfile + tags: precice/ci-spack-pyprecice-deps-1804 diff --git a/.github/workflows/build-spack.yml b/.github/workflows/build-spack.yml index 4ebdcd6a..1d734b7c 100644 --- a/.github/workflows/build-spack.yml +++ b/.github/workflows/build-spack.yml @@ -8,33 +8,18 @@ jobs: build_spack: name: build_spack runs-on: ubuntu-latest - timeout-minutes: 5 - container: benjaminrueth/ci-spack-pyprecice-deps-1804 + timeout-minutes: 15 + container: precice/ci-spack-pyprecice-deps-1804 defaults: run: shell: "bash --login -eo pipefail {0}" steps: - name: Checkout Repository uses: actions/checkout@v2 - - name: Install pip3 + - name: Move Package Script run: | - curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py - python3 get-pip.py - - name: Install jinja2 - run: pip3 install --user jinja2 - - name: Extract branch name # from https://stackoverflow.com/a/58035262/5158031 - shell: bash - run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - id: extract_branch - - name: Specify Package Script + cp spack/repo/packages/py-pyprecice/package.py /py-pyprecice-repo/packages/py-pyprecice/ + cp spack/repo/packages/py-pyprecice/*.patch /py-pyprecice-repo/packages/py-pyprecice/ + - name: Try to build py-pyprecice with spack and test it run: | - python3 spack/jinja-instantiate.py --branch ${{ steps.extract_branch.outputs.branch }} > /opt/spack/var/spack/repos/builtin/packages/py-pyprecice/package.py - ls spack/var/spack/repos/builtin/packages/py-pyprecice - cp spack/var/spack/repos/builtin/packages/py-pyprecice/*.patch /opt/spack/var/spack/repos/builtin/packages/py-pyprecice/ - ls spack/var/spack/repos/builtin/packages/py-pyprecice - - name: Try to build py-pyprecice with spack - run: | - . /opt/spack/share/spack/setup-env.sh && spack env activate precice && spack add py-pyprecice@develop ^precice@develop && spack concretize -f && spack install -v - - name: Import precice and print version - run: | - . /opt/spack/share/spack/setup-env.sh && spack env activate precice && python3 -c "import precice; print(precice.__version__)" + . /opt/spack/share/spack/setup-env.sh && spack env activate ci && spack arch && spack find && spack dev-build py-pyprecice@develop target=x86_64 && spack load precice py-numpy py-mpi4py py-cython openssh openmpi && mkdir runner && cd runner && python3 -c "import precice; print(precice.__version__)" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1800a966..b6e35e9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## 2.2.0.2 + +* Improved error messgaes for all assertions. https://github.com/precice/python-bindings/pull/9 +* Improve CI w.r.t spack package. https://github.com/precice/python-bindings/pull/89 + ## 2.2.0.1 * Format complete codebase according to PEP8 and test formatting. https://github.com/precice/python-bindings/pull/82 diff --git a/README.md b/README.md index c48f23e5..193a3c54 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ Python language bindings for the C++ library preCICE This package provides python language bindings for the C++ library [preCICE](https://github.com/precice/precice). Note that the first three digits of the version number of the bindings indicate the preCICE version that the bindings support. The last digit represents the version of the bindings. Example: `v2.0.0.1` and `v2.0.0.2` of the bindings represent versions `1` and `2` of the bindings that are compatible with preCICE `v2.0.0`. +# User documentation + +Please refer to [precice.org](https://www.precice.org/installation-bindings-python.html) for information on how to install and use the python bindings. Information below is intended for advanced users and developers. + # Required dependencies **preCICE**: Refer to [the preCICE wiki](https://github.com/precice/precice/wiki#1-get-precice) for information on building and installation. @@ -20,7 +24,7 @@ This package provides python language bindings for the C++ library [preCICE](htt We recommend using pip3 (version 19.0.0 or newer required) for the sake of simplicity. You can check your pip3 version via `pip3 --version`. To update pip3, use the following line: ``` -$ pip3 install --upgrade pip +$ pip3 install --user --upgrade pip ``` ## Using pip3 @@ -264,6 +268,20 @@ python setup.py install --user ``` +### ValueError while importing preCICE +If you face the error: + +```bash +ValueError: numpy.ndarray size changed, may indicate binary incompatibility. Expected 88 from C header, got 80 from PyObject +``` + +make sure that you are using an up-to-date version of NumPy. You can update NumPy with + +```bash +pip3 install numpy --upgrade +``` + + # Contributors * [Benjamin RĂ¼th](https://github.com/BenjaminRueth) diff --git a/cyprecice/cyprecice.pyx b/cyprecice/cyprecice.pyx index ad0db10f..0c48fd88 100644 --- a/cyprecice/cyprecice.pyx +++ b/cyprecice/cyprecice.pyx @@ -453,7 +453,7 @@ cdef class Interface: if len(position) > 0: dimensions = len(position) - assert(dimensions == self.get_dimensions()) + assert dimensions == self.get_dimensions(), "Dimensions of vertex coordinate in set_mesh_vertex does not match with dimensions in problem definition. Provided dimensions: {}, expected dimensions: {}".format(dimensions, self.get_dimensions()) elif len(position) == 0: dimensions = self.get_dimensions() @@ -530,7 +530,7 @@ cdef class Interface: if len(positions) > 0: size, dimensions = positions.shape - assert(dimensions == self.get_dimensions()) + assert dimensions == self.get_dimensions(), "Dimensions of vertex coordinates in set_mesh_vertices does not match with dimensions in problem definition. Provided dimensions: {}, expected dimensions: {}".format(dimensions, self.get_dimensions()) elif len(positions) == 0: size = positions.shape[0] dimensions = self.get_dimensions() @@ -640,7 +640,7 @@ cdef class Interface: if len(positions) > 0: size, dimensions = positions.shape - assert(dimensions == self.get_dimensions()) + assert dimensions == self.get_dimensions(), "Dimensions of position coordinates in get_mesh_vertex_ids_from_positions does not match with dimensions in problem definition. Provided dimensions: {}, expected dimensions: {}".format(dimensions, self.get_dimensions()) elif len(positions) == 0: size = positions.shape[0] dimensions = self.get_dimensions() @@ -892,15 +892,15 @@ cdef class Interface: if len(values) > 0: size, dimensions = values.shape - assert(dimensions == self.get_dimensions()) + assert dimensions == self.get_dimensions(), "Dimensions of vector data in write_block_vector_data does not match with dimensions in problem definition. Provided dimensions: {}, expected dimensions: {}".format(dimensions, self.get_dimensions()) if len(values) == 0: size = 0 cdef np.ndarray[int, ndim=1] _vertex_ids = np.ascontiguousarray(vertex_ids, dtype=np.int32) cdef np.ndarray[double, ndim=1] _values = np.ascontiguousarray(values.flatten(), dtype=np.double) - assert(_values.size == size * self.get_dimensions()) - assert(_vertex_ids.size == size) + assert _values.size == size * self.get_dimensions(), "Vector data is not provided for all vertices in write_block_vector_data. Check length of input data provided. Provided size: {}, expected size: {}".format(_values.size, size * self.get_dimensions()) + assert _vertex_ids.size == size, "Vertex IDs are of incorrect length in write_block_vector_data. Check length of vertex ids input. Provided size: {}, expected size: {}".format(_vertex_ids.size, size) self.thisptr.writeBlockVectorData (data_id, size, _vertex_ids.data, _values.data) @@ -941,11 +941,14 @@ cdef class Interface: >>> interface.write_vector_data(data_id, vertex_id, value) """ check_array_like(value, "value", "write_vector_data") + assert len(value) > 0, "Input vector data is empty in write_vector_data" - assert(len(value) > 0) dimensions = len(value) - assert(dimensions == self.get_dimensions()) + + assert dimensions == self.get_dimensions(), "Dimensions of vector data in write_vector_data does not match with dimensions in problem definition. Provided dimensions: {}, expected dimensions: {}".format(dimensions, self.get_dimensions()) + cdef np.ndarray[np.double_t, ndim=1] _value = np.ascontiguousarray(value, dtype=np.double) + self.thisptr.writeVectorData (data_id, vertex_id, _value.data) def write_block_scalar_data (self, data_id, vertex_ids, values): @@ -985,13 +988,11 @@ cdef class Interface: if len(values) == 0: size = 0 - cdef np.ndarray[int, ndim=1] _vertex_ids = np.ascontiguousarray(vertex_ids, dtype=np.int32) cdef np.ndarray[double, ndim=1] _values = np.ascontiguousarray(values, dtype=np.double) - assert(_values.size == size) - assert(_vertex_ids.size == size) - + assert _values.size == size, "Scalar data is not provided for all vertices in write_block_scalar_data. Check size of input data provided. Provided size: {}, expected size: {}".format(_values.size, size) + assert _vertex_ids.size == size, "Vertex IDs are of incorrect length in write_block_scalar_data. Check size of vertex ids input. Provided size: {}, expected size: {}".format(_vertex_ids.size, size) self.thisptr.writeBlockScalarData (data_id, size, _vertex_ids.data, _values.data) def write_scalar_data (self, data_id, vertex_id, double value): diff --git a/docs/ReleaseGuide.md b/docs/ReleaseGuide.md index 55b997ce..18dcbe35 100644 --- a/docs/ReleaseGuide.md +++ b/docs/ReleaseGuide.md @@ -1,25 +1,25 @@ ## Guide to release new version of python-bindings The developer who is releasing a new version of the python-bindings is expected to follow this work flow: -The release of the `python-bindings` repository is made directly from a release branch called `release_v2.1.1.1`. This branch is mainly needed to help other developers with testing. +The release of the `python-bindings` repository is made directly from a release branch called `python-bindings-v2.1.1.1`. This branch is mainly needed to help other developers with testing. -1. Create a branch called `release_v2.1.1.1` from the latest commit of the `develop` branch. +1. Create a branch called `python-bindings-v2.1.1.1` from the latest commit of the `develop` branch. -2. [Open a Pull Request `master` <-- `release_v2.1.1.1`](https://github.com/precice/python-bindings/compare/master...master) named after the version (i.e. `Release v2.1.1.1`) and briefly describe the new features of the release in the PR description. +2. [Open a Pull Request `master` <-- `python-bindings-v2.1.1.1`](https://github.com/precice/python-bindings/compare/master...master) named after the version (i.e. `Release v2.1.1.1`) and briefly describe the new features of the release in the PR description. 3. Bump the version in the following places: - * `CHANGELOG.md` on `release v2.1.1.1`. + * `CHANGELOG.md` on `python-bindings-v2.1.1.1`. * There is no need to bump the version anywhere else, since we use the [python-versioneer](https://github.com/python-versioneer/python-versioneer/) for maintaining the version everywhere else. 4. [Draft a New Release](https://github.com/precice/python-bindings/releases/new) in the `Releases` section of the repository page in a web browser. The release tag needs to be the exact version number (i.e.`v2.1.1.1` or `v2.1.1.1rc1`, compare to [existing tags](https://github.com/precice/python-bindings/tags)). Use `@target:master`. Release title is also the version number (i.e. `v2.1.1.1` or `v2.1.1.1rc1`, compare to [existing releases](https://github.com/precice/python-bindings/tags)). * *Note:* We use the [python-versioneer](https://github.com/python-versioneer/python-versioneer/) for maintaining the version. Therefore the tag directly defines the version in all relevant places. - * *Note:* If it is a pre-release then the option *This is a pre-release* needs to be selected at the bottom of the page. Use `@target:release_v2.1.1.1` for a pre-release, since we will never merge a pre-release into master. + * *Note:* If it is a pre-release then the option *This is a pre-release* needs to be selected at the bottom of the page. Use `@target:python-bindings-v2.1.1.1` for a pre-release, since we will never merge a pre-release into master. a) If a pre-release is made: Directly hit the "Publish release" button in your Release Draft. Now you can check the artifacts (e.g. release on [PyPI](https://pypi.org/project/pyprecice/#history)) of the release. *Note:* As soon as a new tag is created github actions will take care of deploying the new version on PyPI using [this workflow](https://github.com/precice/python-bindings/actions?query=workflow%3A%22Upload+Python+Package%22). - b) If this is a "real" release: As soon as one approving review is made, merge the release PR (from `release_v2.1.1.1`) into `master`. + b) If this is a "real" release: As soon as one approving review is made, merge the release PR (from `python-bindings-v2.1.1.1`) into `master`. 6. Merge `master` into `develop` for synchronization of `develop`. diff --git a/precice/__init__.py b/precice/__init__.py index 40ff7a75..8c28e592 100644 --- a/precice/__init__.py +++ b/precice/__init__.py @@ -1,7 +1,16 @@ __version__ = "unknown" +import warnings from cyprecice import Interface, action_read_iteration_checkpoint, action_write_iteration_checkpoint, action_write_initial_data, get_version_information + +def SolverInterface(*args): + """ + This is just a dummy function to avoid wrong usage of the interface. Please use precice.Interface, if you want to establish a connection to preCICE. See https://github.com/precice/python-bindings/issues/92 for more information. + """ + warnings.warn("please use precice.Interface to create the interface to C++ preCICE. Note that this function (precice.SolverInterface) does not do anything but throwing this warning. See https://github.com/precice/python-bindings/issues/92 for more information.") + + from ._version import get_versions __version__ = get_versions()['version'] del get_versions diff --git a/solverdummy/requirements.txt b/solverdummy/requirements.txt index de12c412..bd681547 100644 --- a/solverdummy/requirements.txt +++ b/solverdummy/requirements.txt @@ -1,3 +1,3 @@ -pyprecice==2.0 +pyprecice>=2.0 argparse>=1.4 numpy>=1.16 diff --git a/spack/README.md b/spack/README.md index 5135e02b..71c8bc42 100644 --- a/spack/README.md +++ b/spack/README.md @@ -2,20 +2,13 @@ The Spack package `py-pyprecice` provides the python bindings via Spack and was submitted via https://github.com/spack/spack/pull/19558. This folder contains the Spack package script that can be used for testing whether the Spack installation works. -Note that the file `var/spack/repos/builtin/packages/py-pyprecice/package.py` is a template that can be specified using `jinja2`. Run `python3 jinja-instrantiate.py` to do so. You may use the `--branch` argument to specify a branch. Example: -``` -python3 jinja-instantiate --branch feature > my_package_script.py -``` +## Docker image `precice/ci-spack-pyprecice-deps-1804` -## Docker image ci-spack-pyprecice-deps-1804 +The workflow `build_env` in `.github/workflows/build-spack.yml` creates the image `precice/ci-spack-pyprecice-deps-1804`. This image contains all dependencies of `py-pyprecice@develop`. + +The workflow `build_spack` in `.github/workflows/build-spack.yml` uses the image `precice/ci-spack-pyprecice-deps-1804` to reduce build time. The workflow uses the `package.py` from this repository to build the latest version of the bindings and run a small test on this version. -The test `build_spack` in `.github/workflows/build-spack.yml` uses the image `benjaminrueth/ci-spack-pyprecice-deps-1804` to reduce build time. Use the file `ci-spack-pyprecice-deps-1804.dockerfile` from this folder for building this image: -``` -docker build -f ci-spack-pyprecice-deps-1804.dockerfile -t USERNAME/ci-spack-pyprecice-deps-1804.dockerfile . -docker push USERNAME/ci-spack-pyprecice-deps-1804.dockerfile -``` ## When a new spack release is necessary -* Add checksum of newest version(s) to https://github.com/precice/python-bindings/blob/develop/spack/var/spack/repos/builtin/packages/py-pyprecice/package.py. You can get checksum for any released version by running `spack checksum py-pyprecice`. -* Run `python3 jinja-instantiate > package.py` to generate the latest version of `package.py`. -* Use `package.py` together with the patches provided in `python-bindings/spack/var/spack/repos/builtin/packages/py-pyprecice` to [create a pull request for Spack](https://github.com/spack/spack/compare) and submit the new release. +* Add checksum of newest version(s) to https://github.com/precice/python-bindings/blob/develop/spack/repo/packages/py-pyprecice/package.py. You can get checksum for any released version by running `spack checksum py-pyprecice`. +* Use `package.py` together with the patches provided in `python-bindings/spack/repo/packages/py-pyprecice` to [create a pull request for Spack](https://github.com/spack/spack/compare) and submit the new release. diff --git a/spack/ci-spack-pyprecice-deps-1804.dockerfile b/spack/ci-spack-pyprecice-deps-1804.dockerfile index ada55360..fb7c3cfa 100644 --- a/spack/ci-spack-pyprecice-deps-1804.dockerfile +++ b/spack/ci-spack-pyprecice-deps-1804.dockerfile @@ -1,8 +1,11 @@ # Build stage with Spack pre-installed and ready to be used -FROM spack/ubuntu-bionic:latest -RUN spack env create precice -# Install dependencies for precice and py-pyprecice -RUN spack env activate precice \ - && spack add boost@1.74.0 cmake@3.18.4 eigen@3.3.8 openssh petsc@3.14.1 pkgconfig precice@develop py-setuptools py-wheel py-cython py-numpy py-mpi4py \ - && spack concretize -f \ - && spack install +FROM spack/ubuntu-bionic:latest + +# Mount the current sources into the build container +# and build the default environment +ADD ./spack/repo /py-pyprecice-repo +RUN spack --color=always env create --without-view ci && \ + spack --color=always -e ci add py-pyprecice@develop target=x86_64 && \ + spack --color=always -e ci repo add /py-pyprecice-repo && \ + spack --color=always -e ci install --fail-fast --only=dependencies && \ + spack --color=always clean -a diff --git a/spack/jinja-instantiate.py b/spack/jinja-instantiate.py deleted file mode 100644 index e26877b0..00000000 --- a/spack/jinja-instantiate.py +++ /dev/null @@ -1,15 +0,0 @@ -import jinja2 -import argparse -from os import path - -parser = argparse.ArgumentParser() -parser.add_argument('--branch', type=str, help="Branch to be checked out", - default="develop") -args = parser.parse_args() - -f = open(path.join(path.dirname(__file__), - "var/spack/repos/builtin/packages/py-pyprecice/package.py"), - "r") - -template = jinja2.Template(f.read()) -print(template.render(branch=args.branch)) diff --git a/spack/var/spack/repos/builtin/packages/py-pyprecice/deactivate-version-check-via-pip.patch b/spack/repo/packages/py-pyprecice/deactivate-version-check-via-pip.patch similarity index 100% rename from spack/var/spack/repos/builtin/packages/py-pyprecice/deactivate-version-check-via-pip.patch rename to spack/repo/packages/py-pyprecice/deactivate-version-check-via-pip.patch diff --git a/spack/var/spack/repos/builtin/packages/py-pyprecice/package.py b/spack/repo/packages/py-pyprecice/package.py similarity index 73% rename from spack/var/spack/repos/builtin/packages/py-pyprecice/package.py rename to spack/repo/packages/py-pyprecice/package.py index 07e1ea48..65817306 100644 --- a/spack/var/spack/repos/builtin/packages/py-pyprecice/package.py +++ b/spack/repo/packages/py-pyprecice/package.py @@ -1,4 +1,4 @@ -# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other +# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) @@ -15,11 +15,12 @@ class PyPyprecice(PythonPackage): homepage = "https://www.precice.org" git = "https://github.com/precice/python-bindings.git" url = "https://github.com/precice/python-bindings/archive/v2.0.0.1.tar.gz" - maintainers = ["ajaust", "BenjaminRueth"] + maintainers = ["ajaust", "BenjaminRodenberg"] # Always prefer final version of release candidate - version("develop", branch="{{ branch }}") - version('2.1.1.2', sha256='363eb3eeccf964fd5ee87012c1032353dd1518662868f2b51f04a6d8a7154045') + version("develop", branch="develop") + version('2.2.0.1', sha256='032fa58193cfa69e3be37557977056e8f507d89b40c490a351d17271269b25ad') + version('2.1.1.2', sha256='363eb3eeccf964fd5ee87012c1032353dd1518662868f2b51f04a6d8a7154045') version("2.1.1.1", sha256="972f574549344b6155a8dd415b6d82512e00fa154ca25ae7e36b68d4d2ed2cf4") version("2.1.0.1", sha256="ac5cb7412c6b96b08a04fa86ea38e52d91ea739a3bd1c209baa93a8275e4e01a") version("2.0.2.1", sha256="c6fca26332316de041f559aecbf23122a85d6348baa5d3252be4ddcd5e94c09a") @@ -27,16 +28,13 @@ class PyPyprecice(PythonPackage): version("2.0.0.2", sha256="5f055d809d65ec2e81f4d001812a250f50418de59990b47d6bcb12b88da5f5d7") version("2.0.0.1", sha256="96eafdf421ec61ad6fcf0ab1d3cf210831a815272984c470b2aea57d4d0c9e0e") - # Import module as a test - import_modules = ["precice"] - + # Older versions of the bindings checked versions via pip. This patch + # removes the pip dependency. + # See also https://github.com/spack/spack/pull/19558 patch("deactivate-version-check-via-pip.patch", when="@:2.1.1.1") - variant("mpi", default=True, description="Enables MPI support") - - depends_on("mpi", when="+mpi") - depends_on("openssh") - depends_on("precice", when="@develop") + depends_on("precice@develop", when="@develop") + depends_on("precice@2.2.0", when="@2.2.0.1:2.2.0.99") depends_on("precice@2.1.1", when="@2.1.1.1:2.1.1.99") depends_on("precice@2.1.0", when="@2.1.0.1:2.1.0.99") depends_on("precice@2.0.2", when="@2.0.2.1:2.0.2.99") @@ -45,9 +43,8 @@ class PyPyprecice(PythonPackage): depends_on("python@3:", type=("build", "run")) depends_on("py-setuptools", type="build") - depends_on("py-wheel", type="build") depends_on("py-numpy", type=("build", "run")) - depends_on("py-mpi4py", type=("build", "run"), when="+mpi") + depends_on("py-mpi4py", type=("build", "run")) depends_on("py-cython@0.29:", type=("build")) phases = ['install_lib', 'build_ext', 'install'] @@ -59,5 +56,7 @@ def build_ext_args(self, spec, prefix): ] def install(self, spec, prefix): + # Older versions of the bindings had a non-standard installation routine + # See also https://github.com/spack/spack/pull/19558#discussion_r513123239 if self.version <= Version("2.1.1.1"): self.setup_py("install", "--prefix={0}".format(prefix)) diff --git a/spack/repo/repo.yaml b/spack/repo/repo.yaml new file mode 100644 index 00000000..61517481 --- /dev/null +++ b/spack/repo/repo.yaml @@ -0,0 +1,2 @@ +repo: + namespace: 'spack'