diff --git a/.github/actions/macos_install/action.yml b/.github/actions/macos_install/action.yml new file mode 100644 index 000000000..8cdabd50f --- /dev/null +++ b/.github/actions/macos_install/action.yml @@ -0,0 +1,24 @@ +name: 'MacOS installation commands' + +runs: + using: "composite" + steps: + + - name: Install non-Python dependencies on macOS + run: | + brew install make + brew install open-mpi + brew install hdf5-mpi + brew install libomp + GFORTRAN_HOME=$(which gfortran || true) + echo "GFORTRAN_HOME : $GFORTRAN_HOME" + if [[ ! -f "$GFORTRAN_HOME" ]]; then + gfort=$(find ${PATH//:/\/ } -name 'gfortran-*' -exec basename {} \; | sort | tail -n 1 || true) + echo "Found $gfort" + gfort_path=$(which ${gfort}) + folder=$(dirname ${gfort_path}) + ln -s ${gfort_path} ${folder}/gfortran + fi + echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV + echo "/opt/homebrew/opt/make/libexec/gnubin" >> $GITHUB_PATH + shell: bash diff --git a/.github/actions/ubuntu_install/action.yml b/.github/actions/ubuntu_install/action.yml new file mode 100644 index 000000000..1b04fc189 --- /dev/null +++ b/.github/actions/ubuntu_install/action.yml @@ -0,0 +1,22 @@ +name: 'Ubuntu installation commands' + +runs: + using: "composite" + steps: + + - name: Install non-Python dependencies on Ubuntu + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: gfortran openmpi-bin libopenmpi-dev libhdf5-openmpi-dev + version: 1.0 + execute_install_scripts: true + + # When loading cached apt packages, the default MPI compiler isn't set. + # Workaround is to 'reinstall' openmpi-bin, which doesn't actually perform + # installation (since openmpi-bin already exists), but instead reruns + # `update-alternatives` which fixes the symlinks to mpicc/mpif90. + - name: Reconfigure non-Python dependencies on Ubuntu + run: | + sudo apt-get update + sudo apt-get install --reinstall openmpi-bin libhdf5-openmpi-dev liblapack-dev libblas-dev + shell: bash diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..7f9e1dbb8 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,131 @@ +name: Deploy new version to PyPI + +on: + workflow_run: + workflows: [testing] + branches: [main] + types: + - completed + +jobs: + deployVersion: + runs-on: ubuntu-latest + if: github.repository == 'pyccel/psydac' + steps: + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install dependencies + uses: ./.github/actions/ubuntu_install + + - name: Update build tools + run: | + pip install --upgrade pip + pip install --upgrade build + pip install --upgrade twine + + - name: Build and deploy to PyPI + run: | + echo ${{ github.event.workflow_run.head_branch }} + python -m build --sdist + ls dist/* + twine upload --repository pypi dist/* --non-interactive + shell: bash + env: + TWINE_USERNAME: '__token__' + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + + - name: Install psydac package without extra components + run: | + pip install . + + - name: "Get tag name" + id: tag_name + run: | + version=$(python -c "from psydac import __version__; print(__version__)") + echo "VERSION=${version}" >> $GITHUB_OUTPUT + echo "TAG_NAME=v${version}" >> $GITHUB_OUTPUT + + - name: "Get release notes" + id: release_notes + run: | + echo "## What's Changed" > release_notes.md +# TODO: CHANGELOG parsing can be enabled when file is ready +# START_LINE=$(grep "^## " CHANGELOG.md -n | head -1 | cut -d: -f -1) +# END_LINE=$(grep "^## " CHANGELOG.md -n | head -2 | tail -1 | cut -d: -f -1) +# START_LINE=$((${START_LINE}+1)) +# END_LINE=$((${END_LINE}-1)) +# echo "## What's Changed" > release_notes.md +# sed -n ${START_LINE},${END_LINE}p CHANGELOG.md >> release_notes.md + + - name: "Get contributors" + run: | + # Get relevant commits + LAST_RELEASE_COMMIT=$(git log -2 --pretty=%H | tail -1) + CURRENT_RELEASE_COMMIT=$(git log -1 --pretty=%H) + + # Find any new lines in the AUTHORS file + NEW_CONTRIBUTORS=$(git diff --no-indent-heuristic --unified=0 --no-color ${LAST_RELEASE_COMMIT}..${CURRENT_RELEASE_COMMIT} AUTHORS | { grep "^\+[^+]" || true; } | cut -d ' ' -f 2-) + if [ -n "${NEW_CONTRIBUTORS}" ] + then + # If there are new contributors then add a section with their names + echo "## New Contributors" >> release_notes.md + while IFS= read -r c + do + echo "- ${c}" >> release_notes.md + done <<< "${NEW_CONTRIBUTORS}" + echo "" >> release_notes.md + fi + + # Find the PR which created the release + PR_ID=$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /search/issues?q="${CURRENT_RELEASE_COMMIT}" | jq '.["items"][0]["number"]') + # Extract authors from all commits in the PR + CONTRIBUTORS=$(gh pr view ${PR_ID} --json commits | jq '.["commits"][]["authors"][]["login"]' | tr -d '"' | sort -u) + + # Add a hidden section listing the user names of all authors on commits in this release + echo "
" >> release_notes.md + echo "" >> release_notes.md + echo "## Contributors" >> release_notes.md + for c in ${CONTRIBUTORS} + do + echo "- @$c" >> release_notes.md + done + echo "" >> release_notes.md + echo "
" >> release_notes.md + + # Get the full changelog link + PREVIOUS_TAG=$(gh release list --limit 1 --json tagName | jq '.[]["tagName"]' | tr -d '"') + + echo "" >> release_notes.md + echo "**Full list of changes**: [${PREVIOUS_TAG}..${tag_name}](https://github.com/pyccel/pyccel/compare/${PREVIOUS_TAG}..${tag_name})" >> release_notes.md + + shell: bash + env: + GH_TOKEN: ${{ github.token }} + tag_name: ${{ steps.tag_name.outputs.TAG_NAME }} + + - name: "Update repo tags" + uses: EndBug/latest-tag@latest + with: + ref: ${{ steps.tag_name.outputs.TAG_NAME }} + + - name: "Update releases" + run: | + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/pyccel/pyccel/releases \ + -f "tag_name=${tag_name}" -f "name=Version ${version}" -F "body=@release_notes.md" -F "draft=false" -F "prerelease=false" -F "generate_release_notes=false" + shell: bash + env: + tag_name: ${{ steps.tag_name.outputs.TAG_NAME }} + version: ${{ steps.tag_name.outputs.VERSION }} + GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/deploy_check.yml b/.github/workflows/deploy_check.yml new file mode 100644 index 000000000..dd7b70319 --- /dev/null +++ b/.github/workflows/deploy_check.yml @@ -0,0 +1,101 @@ +name: Check deployment of new version to TestPyPI + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: [main] + +jobs: + deployTest: + runs-on: ubuntu-latest + if: github.repository == 'pyccel/psydac' + steps: + - id: duplicate_check + uses: fkirc/skip-duplicate-actions@v5 + with: + skip_after_successful_duplicate: 'true' + paths: '["psydac/**", "pyproject.toml", "setup.py", ".github/workflows/deploy_check.yml", ".github/actions/**"]' + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Checkout repository + if: steps.duplicate_check.outputs.should_skip != 'true' + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install dependencies + if: steps.duplicate_check.outputs.should_skip != 'true' + uses: ./.github/actions/ubuntu_install + + - name: Update build tools + if: steps.duplicate_check.outputs.should_skip != 'true' + run: | + pip install --upgrade pip + pip install --upgrade build + pip install --upgrade twine + + - name: Build and deploy to TestPyPI + if: steps.duplicate_check.outputs.should_skip != 'true' + run: | + CURRENT_VERSION=$(grep 'version *= *"\([0-9\.]*\)"' pyproject.toml | grep '[0-9\.]*' -o) + TEST_VERSION=${CURRENT_VERSION}.dev$(date +%Y%m%d%H%M%S) + sed -i.bak "s/\"${CURRENT_VERSION}\"/\"${TEST_VERSION}\"/g" pyproject.toml && rm pyproject.toml.bak + echo "TEST_VERSION=${TEST_VERSION}" >> $GITHUB_ENV + # Setup dummy user to save temp version for meson + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + git commit -m "Save temp version" pyproject.toml + python -m build --sdist + ls dist/* + twine check --strict dist/* + twine upload --repository testpypi dist/* --non-interactive --verbose + sleep 60 + shell: bash + env: + TWINE_USERNAME: '__token__' + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} + + - name: Download psydac package from TestPyPI and install it + if: steps.duplicate_check.outputs.should_skip != 'true' + timeout-minutes: 60 + run: | + WAIT=20 + SUCCESS=0 + for i in {1..5}; do + echo "Attempt $i: Installing psydac==${TEST_VERSION}...""" + if pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ psydac[test]=="${TEST_VERSION}" + then + SUCCESS=1 + break + else + echo "Install failed. Retrying in $WAIT seconds..." + sleep $WAIT + fi + done + + # Final check + if [ "$SUCCESS" -ne 1 ]; then + echo "ERROR: Failed to install psydac==${TEST_VERSION}." + exit 1 + fi + shell: bash + + - name: Initialize test directory + if: steps.duplicate_check.outputs.should_skip != 'true' + run: | + mkdir pytest + cp mpi_tester.py pytest + + - name: Run single-process tests with Pytest + if: steps.duplicate_check.outputs.should_skip != 'true' + working-directory: ./pytest + run: | + pytest -n auto --pyargs psydac -m "not parallel and not petsc" diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 41896bbb9..4bc3bf0df 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -31,7 +31,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install non-Python dependencies on Ubuntu run: | sudo apt update diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index c3c2da8a4..61e859f72 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -9,7 +9,6 @@ on: paths: - 'psydac/**' - 'pyproject.toml' - - 'setup.py' - 'pytest.ini' - 'mpi_tester.py' @@ -23,7 +22,6 @@ on: paths: - 'psydac/**' - 'pyproject.toml' - - 'setup.py' - 'pytest.ini' - 'mpi_tester.py' @@ -36,15 +34,15 @@ jobs: fail-fast: false matrix: os: [ ubuntu-24.04, macos-14 ] - python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13' ] + python-version: [ '3.10', '3.11', '3.12', '3.13', '3.14' ] isMerge: - ${{ github.event_name == 'push' && github.ref == 'refs/heads/devel' }} exclude: - - { isMerge: false, python-version: '3.9' , os: macos-14 } - - { isMerge: false, python-version: '3.10', os: ubuntu-24.04 } - - { isMerge: false, python-version: '3.11', os: macos-14 } - - { isMerge: false, python-version: '3.12', os: ubuntu-24.04 } - - { isMerge: false, python-version: '3.13', os: macos-14 } + - { isMerge: false, python-version: '3.10', os: macos-14 } + - { isMerge: false, python-version: '3.11', os: ubuntu-24.04 } + - { isMerge: false, python-version: '3.12', os: macos-14 } + - { isMerge: false, python-version: '3.13', os: ubuntu-24.04 } + - { isMerge: false, python-version: '3.14', os: macos-14 } name: ${{ matrix.os }} / Python ${{ matrix.python-version }} @@ -62,45 +60,14 @@ jobs: cache: 'pip' cache-dependency-path: | pyproject.toml - requirements.txt - requirements_extra.txt - name: Install non-Python dependencies on Ubuntu if: matrix.os == 'ubuntu-24.04' - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: gfortran openmpi-bin libopenmpi-dev libhdf5-openmpi-dev - version: 1.0 - execute_install_scripts: true - - # When loading cached apt packages, the default MPI compiler isn't set. - # Workaround is to 'reinstall' openmpi-bin, which doesn't actually perform - # installation (since openmpi-bin already exists), but instead reruns - # `update-alternatives` which fixes the symlinks to mpicc/mpif90. - - name: Reconfigure non-Python dependencies on Ubuntu - if: matrix.os == 'ubuntu-24.04' - run: | - sudo apt-get update - sudo apt-get install --reinstall openmpi-bin libhdf5-openmpi-dev liblapack-dev libblas-dev + uses: ./.github/actions/ubuntu_install - name: Install non-Python dependencies on macOS if: matrix.os == 'macos-14' - run: | - brew install make - brew install open-mpi - brew install hdf5-mpi - brew install libomp - GFORTRAN_HOME=$(which gfortran || true) - echo "GFORTRAN_HOME : $GFORTRAN_HOME" - if [[ ! -f "$GFORTRAN_HOME" ]]; then - gfort=$(find ${PATH//:/\/ } -name 'gfortran-*' -exec basename {} \; | sort | tail -n 1 || true) - echo "Found $gfort" - gfort_path=$(which ${gfort}) - folder=$(dirname ${gfort_path}) - ln -s ${gfort_path} ${folder}/gfortran - fi - echo "MPI_OPTS=--oversubscribe" >> $GITHUB_ENV - echo "/opt/homebrew/opt/make/libexec/gnubin" >> $GITHUB_PATH + uses: ./.github/actions/macos_install - name: Print information on MPI and HDF5 libraries run: | @@ -133,7 +100,7 @@ jobs: - if: steps.cache-petsc.outputs.cache-hit != 'true' name: Download a specific release of PETSc run: | - git clone --depth 1 --branch v3.23.2 https://gitlab.com/petsc/petsc.git + git clone --depth 1 --branch v3.24.2 https://gitlab.com/petsc/petsc.git - if: steps.cache-petsc.outputs.cache-hit != 'true' name: Install PETSc with complex support diff --git a/.gitignore b/.gitignore index b3cf64508..5883a31f0 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,7 @@ __test__/ .idea # Visual Studio Code workspace files -*.code-workspace \ No newline at end of file +*.code-workspace + +# Meson lock files +*/.wraplock diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c30e31fc5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "subprojects/igakit"] + path = subprojects/igakit + url = https://github.com/dalcinl/igakit/ diff --git a/LICENSE b/LICENSE index e69e65013..26111488b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2023, Psydac Developers. +Copyright (c) 2018-2025, PSYDAC Developers. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8c891942f..6a2692921 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,11 @@ pip install h5py --no-cache-dir --no-binary h5py pip install ./psydac ``` Here `` is the path to the HDF5 root folder, such that `/lib/` contains the HDF5 dynamic libraries with MPI support. -For an editable install, the `-e/--editable` flag should be provided to the last command above. +For an editable install, the last command above should be replaced with: +```bash +pip install meson-python "pyccel>=2.1.0" +pip install --no-build-isolation --editable ./psydac +``` Again, for more details we refer to our [documentation](https://github.com/pyccel/psydac/blob/devel/docs/installation.md). @@ -94,7 +98,8 @@ python /mpi_tester.py --pyargs psydac -m "parallel and petsc" Many of PSYDAC's low-level Python functions can be translated to a compiled language using the [Pyccel](https://github.com/pyccel/pyccel) transpiler. Currently, all of those functions are collected in modules which follow the name pattern `[module]_kernels.py`. -The classical installation translates all kernel files to Fortran without user intervention. This does not happen in the case of an editable install, but the command `psydac-accelerate` is made available to the user instead. This command applies Pyccel to all the kernel files in the source directory. The default language is currently Fortran, C should also be supported in a near future. +For both classical and editable installations, all kernel files are translated to Fortran without user intervention. If the user adds or edits a kernel file within an editable install, they should use the command `psydac-accelerate` in order to be able to see the changes at runtime. +This command applies Pyccel to all the kernel files in the source directory. The default language is Fortran, and C is also available. - **Only in development mode**: ```bash diff --git a/docs/installation.md b/docs/installation.md index e71e4ddef..704184420 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -8,7 +8,7 @@ ## Requirements -Psydac requires a certain number of components to be installed on the machine: +PSYDAC requires a certain number of components to be installed on the machine: - Fortran and C compilers with OpenMP support - OpenMP library @@ -81,7 +81,7 @@ It is good practice to keep `pip` up to date with pip install --upgrade pip ``` -One can clone the Psydac repository at any location `` in the filesystem which does not require administrator privileges, using either +One can clone the PSYDAC repository at any location `` in the filesystem which does not require administrator privileges, using either ```sh git clone https://github.com/pyccel/psydac.git ``` @@ -93,7 +93,7 @@ The latter command requires a GitHub account. ## Installing the library -Psydac depends on several Python packages, which should be installed in the newly created virtual environment. +PSYDAC depends on several Python packages, which should be installed in the newly created virtual environment. Almost all of these dependencies will be automatically installed by `pip` at the time of installing the `psydac` package later on. The single exception is the `h5py` package, which needs to be installed in parallel mode. @@ -146,7 +146,7 @@ export HDF5_MPI="ON" pip install h5py --no-cache-dir --no-binary h5py ``` -At this point the Psydac library may be installed from the cloned directory `/psydac` in **standard mode**, which copies the relevant files to the correct locations of the virtual environment, or in **development mode**, which only installs symbolic links to the Psydac directory. The latter mode allows one to affect the behavior of Psydac by modifying the source files. +At this point the PSYDAC library may be installed from the cloned directory `/psydac` in **standard mode**, which copies the relevant files to the correct locations of the virtual environment, or in **development mode**, which only installs symbolic links to the PSYDAC directory. The latter mode allows one to affect the behavior of PSYDAC by modifying the source files. - **Standard mode**: ```bash @@ -155,18 +155,19 @@ At this point the Psydac library may be installed from the cloned directory `=2.1.0" + pip install --no-build-isolation --editable . ``` ## Optional PETSc installation -Although Psydac provides several iterative linear solvers which work with our native matrices and vectors, it is often useful to access a dedicated library like [PETSc](https://petsc.org). To this end, our matrices and vectors have the method `topetsc()`, which converts them to the corresponding `petsc4py` objects. -(`petsc4py` is a Python package which provides Python bindings to PETSc.) After solving the linear system with a PETSc solver, the function `petsc_to_psydac` allows converting the solution vector back to the Psydac format. +Although PSYDAC provides several iterative linear solvers which work with our native matrices and vectors, it is often useful to access a dedicated library like [PETSc](https://petsc.org). To this end, our matrices and vectors have the method `topetsc()`, which converts them to the corresponding `petsc4py` objects. +(`petsc4py` is a Python package which provides Python bindings to PETSc.) After solving the linear system with a PETSc solver, the function `petsc_to_psydac` allows converting the solution vector back to the PSYDAC format. In order to use these additional feature, PETSc and petsc4py must be installed as follows. First, we download the latest release of PETSc from its [official Git repository](https://gitlab.com/petsc/petsc): ```sh -git clone --depth 1 --branch v3.21.4 https://gitlab.com/petsc/petsc.git +git clone --depth 1 --branch v3.24.2 https://gitlab.com/petsc/petsc.git ``` Next, we specify a configuration for complex numbers, and install PETSc in a local directory: ```sh diff --git a/docs/output.md b/docs/output.md index 3ce4bb540..79148e050 100644 --- a/docs/output.md +++ b/docs/output.md @@ -1,6 +1,6 @@ -# Psydac's outputs +# PSYDAC's outputs ## Structure -Psydac has a class meant to take care of outputing simulation results. This class, named `OuputManager` is located in `psydac/api/postprocessing.py`. +PSYDAC has a class meant to take care of outputing simulation results. This class, named `OuputManager` is located in `psydac/api/postprocessing.py`. It writes `FemSpace` related information in the Yaml syntax. The file looks like this: ```yaml ndim: 2 @@ -102,7 +102,7 @@ file.h5 ... snapshot_n/ ``` -In addition to that, Psydac also features the `PostProcessManager` class to read those files, recreate all the `FemSpace` and `FemField` objects and export them to `VTK`. +In addition to that, PSYDAC also features the `PostProcessManager` class to read those files, recreate all the `FemSpace` and `FemField` objects and export them to `VTK`. ## Usage of class `OutputManager` @@ -164,4 +164,4 @@ Further examples are present in the following files: * `examples/poisson_3d_target_torus.py` * `examples/sample_multipatch_parallel.py` * `examples/notebooks/Poisson_non_periodic.ipynb` -* `psydac/api/tests/test_postprocessing.py` \ No newline at end of file +* `psydac/api/tests/test_postprocessing.py` diff --git a/docs/source/conf.py b/docs/source/conf.py index 348a196d2..eec07b73b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,7 +35,7 @@ def fixed_init(self, app): pkg_meta = tomli.load(pyproject)['project'] project = str(pkg_meta['name']) -copyright = '2018-2025, Psydac Developers' +copyright = '2018-2025, PSYDAC Developers' author = str(pkg_meta['authors'][0]['name']) release = str(pkg_meta['version']) diff --git a/docs/source/maintenance.rst b/docs/source/maintenance.rst index a269e58a7..4e8253917 100644 --- a/docs/source/maintenance.rst +++ b/docs/source/maintenance.rst @@ -1,10 +1,10 @@ Contributing to the Docs ======================== -We highly encourage anyone working with Psydac to contribute to the library +We highly encourage anyone working with PSYDAC to contribute to the library by adding new or by improving already existing docstrings. -Psydac's documentation is built using Sphinx and the `numpydoc` extension. +PSYDAC's documentation is built using Sphinx and the `numpydoc` extension. In particular, this means that this documentation is automatically generated from the library's docstrings, and hence is only as good as the docstrings themselves. diff --git a/examples/notebooks/Helmholtz_non_periodic.ipynb b/examples/notebooks/Helmholtz_non_periodic.ipynb index e680e0eb7..fed90fe26 100644 --- a/examples/notebooks/Helmholtz_non_periodic.ipynb +++ b/examples/notebooks/Helmholtz_non_periodic.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Solving Helmholtz's equation on non-periodic topological domains\n", - "In this notebook, we will show how to solve Helmholtz's equation on single patch and multipatch domains. As we are running this in a notebook, everything will be run in serial and hence we are limiting ourselves to a fairly coarse discretization to avoid taking too much time. However, Psydac allows for hybrid MPI + OpenMP parallelization with barely any changed to the code. The lines that are impacted by that will be preceeded by their MPI equivalent\n" + "In this notebook, we will show how to solve Helmholtz's equation on single patch and multipatch domains. As we are running this in a notebook, everything will be run in serial and hence we are limiting ourselves to a fairly coarse discretization to avoid taking too much time. However, PSYDAC allows for hybrid MPI + OpenMP parallelization with barely any changed to the code. The lines that are impacted by that will be preceeded by their MPI equivalent\n" ] }, { @@ -13,7 +13,7 @@ "metadata": {}, "source": [ "## Step 1 : Building the domain\n", - "Psydac uses the powerful topological tools of SymPDE to build a large variety of domains. Here we show how to create a Square." + "PSYDAC uses the powerful topological tools of SymPDE to build a large variety of domains. Here we show how to create a Square." ] }, { diff --git a/examples/notebooks/Poisson_non_periodic.ipynb b/examples/notebooks/Poisson_non_periodic.ipynb index 873b73374..cb640e661 100644 --- a/examples/notebooks/Poisson_non_periodic.ipynb +++ b/examples/notebooks/Poisson_non_periodic.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Solving Poisson's equation on non-periodic topological domains\n", - "In this notebook, we will show how to solve poisson's equation on single patch and multipatch domains. As we are running this in a notebook, everything will be run in serial and hence we are limiting ourselves to a fairly coarse discretization to avoid taking too much time. However, Psydac allows for hybrid MPI + OpenMP parallelization with barely any changed to the code. The lines that are impacted by that will be preceeded by their MPI equivalent" + "In this notebook, we will show how to solve poisson's equation on single patch and multipatch domains. As we are running this in a notebook, everything will be run in serial and hence we are limiting ourselves to a fairly coarse discretization to avoid taking too much time. However, PSYDAC allows for hybrid MPI + OpenMP parallelization with barely any changed to the code. The lines that are impacted by that will be preceeded by their MPI equivalent" ] }, { @@ -13,7 +13,7 @@ "metadata": {}, "source": [ "## Step 1 : Building the domain\n", - "Psydac uses the powerful topological tools of SymPDE to build a large variety of domains. Here we show how to do a quarter annulus using a Square and a polar mapping and how to join two annulus to create a multipatch domain. Left commented is an example of a complex multipatch domain built using SymPDE." + "PSYDAC uses the powerful topological tools of SymPDE to build a large variety of domains. Here we show how to do a quarter annulus using a Square and a polar mapping and how to join two annulus to create a multipatch domain. Left commented is an example of a complex multipatch domain built using SymPDE." ] }, { diff --git a/meson.build b/meson.build new file mode 100644 index 000000000..393112bf7 --- /dev/null +++ b/meson.build @@ -0,0 +1,47 @@ +project( + 'psydac', + 'c', 'fortran', + meson_version: '>=1.1.0', +) + +run_command('pyccel', 'make', '-g', 'psydac/**/*_kernels.py', '--convert-only', + check: true) + +find = find_program('find', required: true) + +pyccel_meson_math_file = run_command(find, '__pyccel__/math', '-name', 'meson.build', + check: true).stdout().strip() +pyccel_meson_cwrapper_file = run_command(find, '__pyccel__/cwrapper', '-name', 'meson.build', + check: true).stdout().strip() +pyccel_meson_files = run_command(find, '__pyccel__/psydac', '-name', 'meson.build', + check: true).stdout().split() + +sed = find_program('sed', required: true) + +run_command(sed, '-i.swp', 's/install: true//g', pyccel_meson_math_file, pyccel_meson_cwrapper_file, + check: true) + +foreach f : pyccel_meson_files + f_parts = f.split('/') + rel_f = f_parts.get(1) + foreach i : range(2, f_parts.length()-1) + rel_f = rel_f / f_parts.get(i) + endforeach + run_command(sed, '-i.swp', 's/install_dir: .*/subdir: \'' + rel_f.replace('/','\/') + '\',/g', f, + check: true) +endforeach + + +igakit_proj = subproject('igakit') + +py = import('python').find_installation(modules: ['numpy'], pure: false) + +subdir('__pyccel__/math') +subdir('__pyccel__/cwrapper') +subdir('__pyccel__/psydac') + +# Install the pure-Python package itself +install_subdir('psydac', install_dir: py.get_install_dir()) +install_subdir('mesh', install_dir: py.get_install_dir()) + + diff --git a/performance/matrix_assembly_speed_log.md b/performance/matrix_assembly_speed_log.md index 996975d26..5f26b596b 100644 --- a/performance/matrix_assembly_speed_log.md +++ b/performance/matrix_assembly_speed_log.md @@ -1,4 +1,4 @@ -New Matrix Assembly for Psydac +New Matrix Assembly for PSYDAC ------------------------------ Here we keep track on the performance of the new assembly algorithm (sum factorization). diff --git a/psydac/api/ast/parser.py b/psydac/api/ast/parser.py index 6b60ced0e..24e4c8213 100644 --- a/psydac/api/ast/parser.py +++ b/psydac/api/ast/parser.py @@ -107,16 +107,16 @@ def is_scalar_array(var): #============================================================================== def parse(expr, settings, backend=None): """ - A function which takes a Psydac AST and transforms it to a Pyccel AST. + A function which takes a PSYDAC AST and transforms it to a Pyccel AST. - This function takes a Psydac abstract syntax tree (AST) and returns a + This function takes a PSYDAC abstract syntax tree (AST) and returns a Pyccel AST. In turn, this can be translated to Python code through a call to the function `pycode` from `psydac.pyccel.codegen.printing.pycode`. Parameters ---------- expr : Any - Psydac AST, of any type supported by the Parser class. + PSYDAC AST, of any type supported by the Parser class. settings : dict Dictionary that contains number of dimension, mappings and target if provided @@ -133,9 +133,9 @@ def parse(expr, settings, backend=None): #============================================================================== class Parser(object): """ - A Parser which takes a Psydac AST and transforms it to a Pyccel AST. + A Parser which takes a PSYDAC AST and transforms it to a Pyccel AST. - This class takes a Psydac AST and transforms it to the AST of an old and + This class takes a PSYDAC AST and transforms it to the AST of an old and reduced version of Pyccel, which is shipped as `psydac.pyccel`. This "mini-Pyccel" is then used for printing the Python code. diff --git a/psydac/api/fem.py b/psydac/api/fem.py index 6c295cf4d..3c0bc27d2 100644 --- a/psydac/api/fem.py +++ b/psydac/api/fem.py @@ -52,7 +52,7 @@ class DiscreteBilinearForm(BasicDiscrete): """ Discrete bilinear form ready to be assembled into a matrix. - This class represents the concept of a discrete bilinear form in Psydac. + This class represents the concept of a discrete bilinear form in PSYDAC. Instances of this class generate an appropriate matrix assembly kernel, allocate the matrix if not provided, and prepare a list of arguments for the kernel. @@ -852,7 +852,7 @@ class DiscreteLinearForm(BasicDiscrete): """ Discrete linear form ready to be assembled into a vector. - This class represents the concept of a discrete linear form in Psydac. + This class represents the concept of a discrete linear form in PSYDAC. Instances of this class generate an appropriate vector assembly kernel, allocate the vector if not provided, and prepare a list of arguments for the kernel. @@ -1268,7 +1268,7 @@ class DiscreteFunctional(BasicDiscrete): """ Discrete functional ready to be assembled into a scalar (real or complex). - This class represents the concept of a discrete functional in Psydac. + This class represents the concept of a discrete functional in PSYDAC. Instances of this class generate an appropriate functional assembly kernel, and prepare a list of arguments for the kernel. diff --git a/psydac/api/fem_bilinear_form.py b/psydac/api/fem_bilinear_form.py index 27e093ff6..4bd65193a 100644 --- a/psydac/api/fem_bilinear_form.py +++ b/psydac/api/fem_bilinear_form.py @@ -59,7 +59,7 @@ class DiscreteBilinearForm: """ Discrete bilinear form ready to be assembled into a matrix. - This class represents the concept of a discrete bilinear form in Psydac. + This class represents the concept of a discrete bilinear form in PSYDAC. Instances of this class generate an appropriate matrix assembly kernel, allocate the matrix if not provided, and prepare a list of arguments for the kernel. @@ -2140,10 +2140,10 @@ def construct_arguments_generate_assembly_file(self): *list(coupling_terms.values())) # This part is a bit shady. - # There has been a case, where my code wasn't running, because one instance of deep-(Psydac/Sympde/Sympy)-code + # There has been a case, where my code wasn't running, because one instance of deep-(PSYDAC/Sympde/Sympy)-code # correctly understood that a possibly complicated expression (corresponding to a block) in fact evaluates to 0, # and hence no StencilMatrix for that particular block ever needs to be created - but a different part of - # deep-(Psydac/Sympde/Sympy)-code did not get that simplification right (yet?), and decided that the assembly code + # deep-(PSYDAC/SymPDE/SymPy)-code did not get that simplification right (yet?), and decided that the assembly code # needs a StencilMatrix as input for this particular block. # See readBilinearForm for additional information. # This part of the code filters out unnecessary StencilMatrices, such that only the relevant StencilMatrices diff --git a/psydac/api/postprocessing.py b/psydac/api/postprocessing.py index 1fe7fc35f..55d088b0e 100644 --- a/psydac/api/postprocessing.py +++ b/psydac/api/postprocessing.py @@ -2317,7 +2317,7 @@ def _get_mesh(self, mapping, grid, grid_local, local_domain, elif mapping is None: pass else: - raise TypeError(f'mapping need to be SymPDE Mapping or Psydac SplineMapping and not {type(mapping)}') + raise TypeError(f'mapping should be SymPDE Mapping or PSYDAC SplineMapping, not {type(mapping)}') conn, off, typ, i_mpi_dd = self._compute_unstructured_mesh_info( local_domain, npts_per_cell=npts_per_cell, diff --git a/psydac/api/tests/test_api_feec_1d.py b/psydac/api/tests/test_api_feec_1d.py index 4fba17b8a..2142f1a3d 100644 --- a/psydac/api/tests/test_api_feec_1d.py +++ b/psydac/api/tests/test_api_feec_1d.py @@ -131,7 +131,7 @@ class CollelaMapping1D(Mapping): # ... #-------------------------------------------------------------------------- - # Discrete objects: Psydac + # Discrete objects: PSYDAC #-------------------------------------------------------------------------- # Discrete physical domain and discrete DeRham sequence diff --git a/psydac/api/tests/test_api_feec_2d.py b/psydac/api/tests/test_api_feec_2d.py index 931cd03ef..eb16561bf 100644 --- a/psydac/api/tests/test_api_feec_2d.py +++ b/psydac/api/tests/test_api_feec_2d.py @@ -290,7 +290,7 @@ class CollelaMapping2D(Mapping): integral(domain.boundary, 1e30 * cross(u1, nn) * cross(v1, nn))) #-------------------------------------------------------------------------- - # Discrete objects: Psydac + # Discrete objects: PSYDAC #-------------------------------------------------------------------------- if use_spline_mapping: domain_h = discretize(domain, filename=filename, comm=MPI.COMM_WORLD) diff --git a/psydac/api/tests/test_api_feec_3d.py b/psydac/api/tests/test_api_feec_3d.py index 64f05dcdb..fc3a0bdb8 100644 --- a/psydac/api/tests/test_api_feec_3d.py +++ b/psydac/api/tests/test_api_feec_3d.py @@ -112,7 +112,7 @@ def run_maxwell_3d_scipy(logical_domain, mapping, e_ex, b_ex, ncells, degree, pe F = mapping.get_callable_mapping() #------------------------------------------------------------------------------ - # Discrete objects: Psydac + # Discrete objects: PSYDAC #------------------------------------------------------------------------------ # Select backend for acceleration of the generated assembly code @@ -215,7 +215,7 @@ def run_maxwell_3d_stencil(logical_domain, mapping, e_ex, b_ex, ncells, degree, F = mapping.get_callable_mapping() #------------------------------------------------------------------------------ - # Discrete objects: Psydac + # Discrete objects: PSYDAC #------------------------------------------------------------------------------ # Select backend for acceleration of the generated assembly code diff --git a/psydac/api/tests/test_equation.py b/psydac/api/tests/test_equation.py index ce8bdb3f9..80dfe327b 100644 --- a/psydac/api/tests/test_equation.py +++ b/psydac/api/tests/test_equation.py @@ -47,7 +47,7 @@ def test_field_and_constant(backend): equation = find(u, forall=v, lhs=a(u, v), rhs=l(v), bc=bc) - # Discretization and solution with Psydac + # Discretization and solution with PSYDAC ncells = (5, 5) degree = (3, 3) domain_h = discretize(domain, ncells=ncells) diff --git a/psydac/cad/geometry.py b/psydac/cad/geometry.py index 4d41d8421..b0bdfa98c 100644 --- a/psydac/cad/geometry.py +++ b/psydac/cad/geometry.py @@ -514,7 +514,7 @@ def export( self, filename ): def export_nurbs_to_hdf5(filename, nurbs, periodic=None, comm=None ): """ - Export a single-patch igakit NURBS object to a Psydac geometry file in HDF5 format + Export a single-patch igakit NURBS object to a PSYDAC geometry file in HDF5 format Parameters ---------- diff --git a/psydac/cmd/accelerate.py b/psydac/cmd/accelerate.py index cd406590b..febfbc591 100644 --- a/psydac/cmd/accelerate.py +++ b/psydac/cmd/accelerate.py @@ -3,18 +3,19 @@ # LICENSE file or go to https://github.com/pyccel/psydac/blob/devel/LICENSE # # for full license details. # #---------------------------------------------------------------------------# -#The purpose of this file is to be launched after an editable installation of Psydac, to pyccelise all the Psydac kernels. + +#The purpose of this file is to be launched after an editable installation of PSYDAC, to pyccelise all the PSYDAC kernels. #This file is useless during a classic installation because the kernels are already pyccelised in the construction folder. def main(): """ - console command to pyccelise all the Psydac kernels. + console command to pyccelise all the PSYDAC kernels. """ import argparse parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Accelerate all computational kernels in Psydac using Pyccel (editable install only)" + description="Accelerate all computational kernels in PSYDAC using Pyccel (editable install only)" ) # Add Argument --language at the pyccel command @@ -47,7 +48,7 @@ def main(): # get the absolute path to the psydac directory psydac_path = os.path.abspath(psydac.__path__[0]) - print("\nNOTE: This script should only be used if Psydac was installed in editable mode.\n") + print("\nNOTE: This script should only be used if PSYDAC was installed in editable mode.\n") # Define all the parameters of the command in the parameters array parameters = ['--language', args.language] @@ -61,5 +62,5 @@ def main(): for name in files: if name.endswith('_kernels.py'): print(' Pyccelize file: ' + os.path.join(path, name)) - sub_run([shutil.which('pyccel'), os.path.join(path, name), *parameters], shell=False) + sub_run([shutil.which('pyccel'), 'compile', os.path.join(path, name), *parameters], shell=False) print() diff --git a/psydac/fem/splines.py b/psydac/fem/splines.py index c8d7ac069..f36f54015 100644 --- a/psydac/fem/splines.py +++ b/psydac/fem/splines.py @@ -265,7 +265,7 @@ def mapping( self ): """ Assume identity mapping for now. """ # [YG, 28.03.2025]: not clear why there should be no mapping here... - # Clearly this property is never used in Psydac. + # Clearly this property is never used in PSYDAC. return None @property diff --git a/psydac/fem/tensor.py b/psydac/fem/tensor.py index 0b13825ef..3eac8878b 100644 --- a/psydac/fem/tensor.py +++ b/psydac/fem/tensor.py @@ -163,7 +163,7 @@ def domain_decomposition(self): @property def mapping(self): # [YG, 28.03.2025]: not clear why there should be no mapping here... - # Clearly this property is never used in Psydac. + # Clearly this property is never used in PSYDAC. return None @property diff --git a/psydac/linalg/solvers.py b/psydac/linalg/solvers.py index 5066219d2..871e5090e 100644 --- a/psydac/linalg/solvers.py +++ b/psydac/linalg/solvers.py @@ -1007,7 +1007,7 @@ class MinimumResidual(InverseLinearOperator): Notes ----- - This is an adaptation of the MINRES Solver in Scipy, where the method is modified to accept Psydac data structures, + This is an adaptation of the MINRES Solver in Scipy, where the method is modified to accept PSYDAC data structures, https://github.com/scipy/scipy/blob/v1.7.1/scipy/sparse/linalg/isolve/minres.py References @@ -1059,7 +1059,7 @@ def solve(self, b, out=None): Notes ----- - This is an adaptation of the MINRES Solver in Scipy, where the method is modified to accept Psydac data structures, + This is an adaptation of the MINRES Solver in Scipy, where the method is modified to accept PSYDAC data structures, https://github.com/scipy/scipy/blob/v1.7.1/scipy/sparse/linalg/isolve/minres.py References ---------- @@ -1306,7 +1306,7 @@ class LSMR(InverseLinearOperator): Notes ----- - This is an adaptation of the LSMR Solver in Scipy, where the method is modified to accept Psydac data structures, + This is an adaptation of the LSMR Solver in Scipy, where the method is modified to accept PSYDAC data structures, https://github.com/scipy/scipy/blob/v1.7.1/scipy/sparse/linalg/isolve/lsmr.py References @@ -1372,7 +1372,7 @@ def solve(self, b, out=None): Notes ----- - This is an adaptation of the LSMR Solver in Scipy, where the method is modified to accept Psydac data structures, + This is an adaptation of the LSMR Solver in Scipy, where the method is modified to accept PSYDAC data structures, https://github.com/scipy/scipy/blob/v1.7.1/scipy/sparse/linalg/isolve/lsmr.py References diff --git a/psydac/linalg/tests/test_block.py b/psydac/linalg/tests/test_block.py index 974037706..f7b95db74 100644 --- a/psydac/linalg/tests/test_block.py +++ b/psydac/linalg/tests/test_block.py @@ -1410,7 +1410,7 @@ def test_block_linear_operator_1d_parallel_topetsc( dtype, n1, p1, P1): y_petsc = Lp.createVecLeft() # Compute dot product Lp.mult(x.topetsc(), y_petsc) - # Cast result back to Psydac BlockVector format + # Cast result back to PSYDAC BlockVector format y_p = petsc_to_psydac(y_petsc, V) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) @@ -1490,7 +1490,7 @@ def test_block_linear_operator_2d_parallel_topetsc( dtype, n1, n2, p1, p2, P1, P y_petsc = Lp.createVecLeft() # Compute dot product Lp.mult(x.topetsc(), y_petsc) - # Cast result back to Psydac BlockVector format + # Cast result back to PSYDAC BlockVector format y_p = petsc_to_psydac(y_petsc, L.codomain) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) diff --git a/psydac/linalg/tests/test_stencil_matrix.py b/psydac/linalg/tests/test_stencil_matrix.py index a2798b2f1..82541afc7 100644 --- a/psydac/linalg/tests/test_stencil_matrix.py +++ b/psydac/linalg/tests/test_stencil_matrix.py @@ -2777,7 +2777,7 @@ def test_stencil_matrix_2d_parallel_topetsc(dtype, n1, n2, p1, p2, sh1, sh2, P1, y_petsc = Mp.createVecLeft() # Compute dot product Mp.mult(x.topetsc(), y_petsc) - # Cast result back to Psydac StencilVector format + # Cast result back to PSYDAC StencilVector format y_p = petsc_to_psydac(y_petsc, V) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) @@ -2851,7 +2851,7 @@ def test_stencil_matrix_1d_parallel_topetsc(dtype, n1, p1, sh1, P1): y_petsc = Mp.createVecLeft() # Compute dot product Mp.mult(x.topetsc(), y_petsc) - # Cast result back to Psydac StencilVector format + # Cast result back to PSYDAC StencilVector format y_p = petsc_to_psydac(y_petsc, V) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) @@ -2906,7 +2906,7 @@ def test_mass_matrix_2d_parallel_topetsc(n1, n2, p1, p2, P1, P2): y_petsc = Mp.createVecLeft() # Compute dot product Mp.mult(x.topetsc(), y_petsc) - # Cast result back to Psydac StencilVector format + # Cast result back to PSYDAC StencilVector format y_p = petsc_to_psydac(y_petsc, Vh.coeff_space) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) @@ -2965,7 +2965,7 @@ def test_mass_matrix_3d_parallel_topetsc(n1, n2, n3, p1, p2, p3, P1, P2, P3): y_petsc = Mp.createVecLeft() # Compute dot product Mp.mult(x.topetsc(), y_petsc) - # Cast result back to Psydac StencilVector format + # Cast result back to PSYDAC StencilVector format y_p = petsc_to_psydac(y_petsc, Vh.coeff_space) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) @@ -3019,7 +3019,7 @@ def test_mass_matrix_1d_parallel_topetsc(n1, p1, P1): x_petsc = x.topetsc() # Compute dot product Mp.mult(x_petsc, y_petsc) - # Cast result back to Psydac StencilVector format + # Cast result back to PSYDAC StencilVector format y_p = petsc_to_psydac(y_petsc, Vh.coeff_space) assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12) diff --git a/psydac/linalg/topetsc.py b/psydac/linalg/topetsc.py index 91421264a..8487a03a8 100644 --- a/psydac/linalg/topetsc.py +++ b/psydac/linalg/topetsc.py @@ -95,12 +95,12 @@ def petsc_local_to_psydac( V : VectorSpace, petsc_index : int): """ - Convert the PETSc local index (starting from 0 in each process) to a Psydac local index (natural multi-index, as grid coordinates). + Convert the PETSc local index (starting from 0 in each process) to a PSYDAC local index (natural multi-index, as grid coordinates). Parameters ----------- V : VectorSpace - The vector space to which the Psydac vector belongs. + The vector space to which the PSYDAC vector belongs. This defines the number of blocks, the size of each block, and how each block is distributed across MPI processes. @@ -110,9 +110,9 @@ def petsc_local_to_psydac( Returns -------- block: tuple - The block where the Psydac multi-index belongs to. + The block where the PSYDAC multi-index belongs to. psydac_index : tuple - The Psydac local multi-index. This index is local the block. + The PSYDAC local multi-index. This index is local the block. """ # Get the number of points for each block and each dimension local to the current process: @@ -168,12 +168,12 @@ def psydac_to_petsc_global( block_indices, ndarray_indices) -> int: """ - Convert the Psydac local index (natural multi-index, as grid coordinates) to a PETSc global index. Performs a search to find the process owning the multi-index. + Convert the PSYDAC local index (natural multi-index, as grid coordinates) to a PETSc global index. Performs a search to find the process owning the multi-index. Parameters ----------- V : VectorSpace - The vector space to which the Psydac vector belongs. + The vector space to which the PSYDAC vector belongs. This defines the number of blocks, the size of each block, and how each block is distributed across MPI processes. @@ -277,7 +277,7 @@ def get_npts_local(V : VectorSpace) -> list: Parameter --------- V : VectorSpace - The distributed Psydac vector space. + The distributed PSYDAC vector space. Returns -------- @@ -310,7 +310,7 @@ def get_npts_per_block(V : VectorSpace) -> list: Parameter --------- V : VectorSpace - The distributed Psydac vector space. + The distributed PSYDAC vector space. Returns -------- @@ -338,12 +338,12 @@ def get_npts_per_block(V : VectorSpace) -> list: def vec_topetsc(vec): - """ Convert vector from Psydac format to a PETSc.Vec object. + """ Convert vector from PSYDAC format to a PETSc.Vec object. Parameters ---------- vec : psydac.linalg.stencil.StencilVector | psydac.linalg.block.BlockVector - Psydac StencilVector or BlockVector. In the case of a BlockVector, only the case where the blocks are StencilVector is implemented. + PSYDAC StencilVector or BlockVector. In the case of a BlockVector, only the case where the blocks are StencilVector is implemented. Returns ------- @@ -438,12 +438,12 @@ def vec_topetsc(vec): def mat_topetsc(mat): - """ Convert operator from Psydac format to a PETSc.Mat object. + """ Convert operator from PSYDAC format to a PETSc.Mat object. Parameters ---------- mat : psydac.linalg.stencil.StencilMatrix | psydac.linalg.block.BlockLinearOperator - Psydac operator. In the case of a BlockLinearOperator, only the case where the blocks are StencilMatrix is implemented. + PSYDAC operator. In the case of a BlockLinearOperator, only the case where the blocks are StencilMatrix is implemented. Returns ------- diff --git a/psydac/linalg/utilities.py b/psydac/linalg/utilities.py index 117d3b429..db86e075d 100644 --- a/psydac/linalg/utilities.py +++ b/psydac/linalg/utilities.py @@ -21,8 +21,8 @@ #============================================================================== def array_to_psydac(x, V): """ - Convert a NumPy array to a Vector of the space V. This function is designed to be the inverse of the method .toarray() of the class Vector. - Note: This function works in parallel but it is very costly and should be avoided if performance is a priority. + Convert a NumPy array to a Vector of the space V. This function is designed + to be the inverse of the method .toarray() of the class Vector. Parameters ---------- @@ -30,15 +30,18 @@ def array_to_psydac(x, V): Array to be converted. It only contains the true data, the ghost regions must not be included. V : psydac.linalg.stencil.StencilVectorSpace or psydac.linalg.block.BlockVectorSpace - Space of the final Psydac Vector. + Space of the final PSYDAC Vector. Returns ------- u : psydac.linalg.stencil.StencilVector or psydac.linalg.block.BlockVector Element of space V, the coefficients of which (excluding ghost regions) are the entries of x. The ghost regions of u are up to date. + Notes + ----- + This function works in parallel but it is very costly and should be avoided + if performance is a priority. """ - assert x.ndim == 1, 'Array must be 1D.' if x.dtype==complex: assert V.dtype==complex, 'Complex array cannot be converted to a real StencilVector' @@ -80,8 +83,9 @@ def _array_to_psydac_recursive(x, u): #============================================================================== def petsc_to_psydac(x, Xh, out=None): """ - Convert a PETSc.Vec object to a StencilVector or BlockVector. It assumes that PETSc was installed with the configuration for complex numbers. - Uses the index conversion functions in psydac.linalg.topetsc.py. + Convert a PETSc.Vec object to a StencilVector or BlockVector. It assumes + that PETSc was installed with the configuration for complex numbers. It + uses the index conversion functions in psydac.linalg.topetsc. Parameters ---------- @@ -89,17 +93,17 @@ def petsc_to_psydac(x, Xh, out=None): PETSc vector Xh : psydac.linalg.stencil.StencilVectorSpace | psydac.linalg.block.BlockVectorSpace - Space of the coefficients of the Psydac vector. + Space of the coefficients of the PSYDAC vector. out : psydac.linalg.stencil.StencilVector | psydac.linalg.block.BlockVector, optional - The Psydac vector where to store the result. + The PSYDAC vector where to store the result. Returns ------- u : psydac.linalg.stencil.StencilVector | psydac.linalg.block.BlockVector - Psydac vector. In the case of a BlockVector, the blocks must be StencilVector. The general case is not yet implemented. + PSYDAC vector. In the case of a BlockVector, the blocks must be of type + StencilVector. The general case is not yet implemented. """ - if isinstance(Xh, BlockVectorSpace): if any([isinstance(Xh.spaces[b], BlockVectorSpace) for b in range(len(Xh.spaces))]): raise NotImplementedError('Block of blocks not implemented.') diff --git a/psydac/mapping/tests/test_discrete_mapping.py b/psydac/mapping/tests/test_discrete_mapping.py index ae5c047a5..a17c9e174 100644 --- a/psydac/mapping/tests/test_discrete_mapping.py +++ b/psydac/mapping/tests/test_discrete_mapping.py @@ -288,7 +288,7 @@ def test_nurbs_circle(): control = disk.points d = disk.degree - # Psydac + # PSYDAC spaces = [SplineSpace(degree, knot) for degree, knot in zip(d, k)] ncells = [len(space.breaks)-1 for space in spaces] diff --git a/psydac/pyccel/ast/core.py b/psydac/pyccel/ast/core.py index 05f893060..20ec62612 100644 --- a/psydac/pyccel/ast/core.py +++ b/psydac/pyccel/ast/core.py @@ -3412,7 +3412,7 @@ def __init__( # arguments - # Removed by JO in 06/23 in PR #303: This TypeError was raised upon Sphinx trying to import several of Psydac's modules. + # Removed by JO in 06/23 in PR #303: This TypeError was raised upon Sphinx trying to import several of PSYDAC's modules. #if not iterable(arguments): # raise TypeError('arguments must be an iterable') diff --git a/psydac/version.py b/psydac/version.py index 567ce4fc2..3652431d0 100644 --- a/psydac/version.py +++ b/psydac/version.py @@ -3,4 +3,33 @@ # LICENSE file or go to https://github.com/pyccel/psydac/blob/devel/LICENSE # # for full license details. # #---------------------------------------------------------------------------# -__version__ = "0.1" +def extract_version() -> str: + """ + Returns either the version of the installed package (e.g. "0.17.1") or the + one found in pyproject.toml (e.g. "0.17.1-dev (at /project/location)"). + + Returns + ------- + str + The package version. + + See also + -------- + https://stackoverflow.com/a/76206192 + + """ + from contextlib import suppress + from pathlib import Path + + with suppress(FileNotFoundError, StopIteration): + root_dir = Path(__file__).parent.parent + with open(root_dir / "pyproject.toml", encoding="utf-8") as pyproject_toml: + version_line = next(line for line in pyproject_toml if line.startswith("version")) + version = version_line.split("=")[1].strip("'\"\n ") + return f"{version}-dev (at {root_dir})" + + import importlib.metadata + return importlib.metadata.version(__package__) + + +__version__ = extract_version() diff --git a/pyproject.toml b/pyproject.toml index b341f03c7..a9f88261e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,17 @@ [build-system] -requires = ["setuptools >= 64.0, != 67.2.0", "wheel", "numpy", "pyccel >= 2.0.1"] -build-backend = "setuptools.build_meta" +requires = ["meson-python", "pyccel >= 2.1.0"] +build-backend = "mesonpy" [project] name = "psydac" -version = "0.1" description = "Python package for isogeometric analysis (IGA)" readme = "README.md" -requires-python = ">= 3.9" -license = {file = "LICENSE"} +license = "MIT" +license-files = ["LICENSE", "AUTHORS"] +version = "0.1" +requires-python = ">= 3.10" authors = [ - {name = "Psydac development team", email = "psydac@googlegroups.com"} + {name = "PSYDAC development team", email = "psydac@googlegroups.com"} ] maintainers = [ {name = "Yaman Güçlü", email = "yaman.guclu@gmail.com"}, @@ -21,7 +22,7 @@ keywords = ["FEM", "IGA", "B-spline", "NURBS"] classifiers = ["Programming Language :: Python :: 3"] dependencies = [ # Third-party packages from PyPi - 'numpy >= 1.16', + 'numpy >= 1.16, < 2.4', 'scipy >= 1.12', 'sympy >= 1.5', 'matplotlib', @@ -31,7 +32,7 @@ dependencies = [ # Our packages from PyPi 'sympde == 0.19.2', - 'pyccel >= 2.0.1', + 'pyccel >= 2.1.0', 'gelato == 0.12', # MPI for Python provides Python bindings for the @@ -50,9 +51,6 @@ dependencies = [ # When pyccel is run in parallel with MPI, it uses tblib to pickle # tracebacks, which allows mpi4py to broadcast exceptions 'tblib', - - # IGAKIT - not on PyPI - 'igakit @ https://github.com/dalcinl/igakit/archive/refs/heads/master.zip' ] [project.optional-dependencies] @@ -72,13 +70,10 @@ Repository = "https://github.com/pyccel/psydac.git" psydac-mesh = "psydac.cmd.mesh:main" psydac-accelerate = "psydac.cmd.accelerate:main" -[tool.setuptools.packages.find] -include = ["psydac*"] -exclude = ["*__psydac__*"] -namespaces = false - -[tool.setuptools.package-data] -"*" = ["*.txt", "**/tests/data/*.png"] +[tool.meson-python.args] +setup=['-Ddefault_library=static'] +dist=['--include-subprojects'] +install=['--only=python-modules'] [tool.coverage.run] branch = true diff --git a/setup.py b/setup.py deleted file mode 100644 index 95a40ef22..000000000 --- a/setup.py +++ /dev/null @@ -1,46 +0,0 @@ -import logging -import os -from shutil import which -from subprocess import PIPE, STDOUT # nosec B404 -from subprocess import run as sub_run -from setuptools import setup -from setuptools.command.build_py import build_py - - -class BuildPyCommand(build_py): - """Custom build command to pyccelise _kernels files in the build directory.""" - - # Rewrite the build_module function to copy each module in the build - # repository and pyccelise the modules ending with _kernels - def build_module(self, module, module_file, package): - outfile, copied = super().build_module(module, module_file, package) - - # This part check if the module is pyccelisable and pyccelise it in - # case - if module.endswith('_kernels'): - self.announce(f"\nPyccelising [{module}] ...", level=logging.INFO) - pyccel = sub_run([which('pyccel'), outfile, '--language', 'fortran', '--openmp'], - stdout=PIPE, stderr=STDOUT, - text=True, shell=False, check=True) # nosec B603 - self.announce(pyccel.stdout, level=logging.INFO) - - return outfile, copied - - def run(self): - super().run() - - # Remove __pyccel__ directories - sub_run([which('pyccel-clean'), self.build_lib], shell=False, check=True) # nosec B603, B607 - - # Remove useless .lock files - for path, subdirs, files in os.walk(self.build_lib): - for name in files: - if name == '.lock_acquisition.lock': - os.remove(os.path.join(path, name)) - - -setup( - cmdclass={ - 'build_py': BuildPyCommand, - }, -) diff --git a/subprojects/igakit b/subprojects/igakit new file mode 160000 index 000000000..8f8a64991 --- /dev/null +++ b/subprojects/igakit @@ -0,0 +1 @@ +Subproject commit 8f8a64991b930c62f0ab7d459a55cc17e0a12a22