diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 1da0944a..632b6279 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - name: Install DOLFINx - uses: jorgensd/actions/install-dolfinx@v0.2.0 + uses: jorgensd/actions/install-dolfinx@v0.3 with: dolfinx: main ufl: main @@ -35,7 +35,7 @@ jobs: cmake --install build-dir - name: Install DOLFINx-MPC (Python) - run: python3 -m pip -v install --config-settings=cmake.build-type="Release" --no-build-isolation ./python/[docs] + run: python3 -m pip -v install --break-system-packages --config-settings=cmake.build-type="Release" --no-build-isolation ./python/[docs] - name: Build docs run: jupyter book build . diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9407a66f..9d740479 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,9 +1,11 @@ on: push: - branches: - - "!*" tags: - "v*" + pull_request: + branches: + - release + - main workflow_dispatch: @@ -43,6 +45,18 @@ jobs: - name: Build and push Docker image uses: docker/build-push-action@v5 + with: + context: . + load: true + push: false + file: docker/Dockerfile + platforms: linux/amd64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') with: context: . push: true diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index ed8ab9c6..c6c00379 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -32,6 +32,9 @@ jobs: run: | apt-get -y update apt-get install unzip + - name: Update pip + run: | + python3 -m pip install --break-system-packages --upgrade pip setuptools - name: Set up JDK 17 uses: actions/setup-java@v4 @@ -64,13 +67,13 @@ jobs: echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH - name: Install DOLFINx - uses: jorgensd/actions/install-dolfinx@v0.2.0 + uses: jorgensd/actions/install-dolfinx@v0.3 with: petsc_arch: ${PETSC_ARCH} - dolfinx: v0.8.0 - basix: v0.8.0 - ufl: 2024.1.0.post0 - ffcx: v0.8.0 + dolfinx: main + basix: main + ufl: main + ffcx: main - name: Run build-wrapper run: | @@ -84,7 +87,7 @@ jobs: cmake --install build-dir - name: Install DOLFINx-MPC (Python) - run: python3 -m pip -v install --config-settings=cmake.build-type="Release" --no-build-isolation ./python + run: python3 -m pip -v install --break-system-packages --config-settings=cmake.build-type="Release" --no-build-isolation ./python - name: Run sonar-scanner env: diff --git a/.github/workflows/test_mpc.yml b/.github/workflows/test_mpc.yml index 4539e36c..273291b0 100644 --- a/.github/workflows/test_mpc.yml +++ b/.github/workflows/test_mpc.yml @@ -56,7 +56,7 @@ jobs: HDF5_DIR: "/usr/local/" MPC_BUILD_MODE: ${{ matrix.build_mode }} MPC_CMAKE_CXX_FLAGS: "-Wall -Werror -g -pedantic -Ofast -march=native" - PYTHONPATH: "/usr/local/dolfinx-${PETSC_TYPE}/lib/python3.10/dist-packages:/usr/local/lib" + PYTHONPATH: "/usr/local/dolfinx-${PETSC_TYPE}/lib/python3.12/dist-packages:/usr/local/lib" LD_LIBRARY_PATH: "/usr/local/petsc/${PETSC_ARCH}/lib/:/usr/local" DEB_PYTHON_INSTALL_LAYOUT: deb_system steps: @@ -69,7 +69,7 @@ jobs: apt-get install -y clang - name: upgrade pip - run: python3 -m pip install --upgrade setuptools pip + run: python3 -m pip install --break-system-packages --upgrade setuptools pip - name: Check formatting run: | @@ -78,16 +78,16 @@ jobs: - name: Check typing run: | - python3 -m pip install mypy + python3 -m pip install --break-system-packages mypy cd python python3 -m mypy . --exclude=build - name: Install h5py run: | - python3 -m pip install --no-cache-dir --no-binary=h5py h5py + python3 -m pip install --no-build-isolation --break-system-packages --no-cache-dir --no-binary=h5py h5py - name: Install DOLFINx - uses: jorgensd/actions/install-dolfinx@v0.2.0 + uses: jorgensd/actions/install-dolfinx@v0.3 with: dolfinx: ${{ env.DOLFINX_BRANCH }} ufl: ${{ env.UFL_BRANCH }} @@ -102,7 +102,7 @@ jobs: cmake --install build-dir - name: Install DOLFINx-MPC (Python) - run: python3 -m pip -v install --config-settings=cmake.build-type=${MPC_BUILD_MODE} --no-build-isolation -e python/[test] + run: python3 -m pip -v install --break-system-packages --config-settings=cmake.build-type=${MPC_BUILD_MODE} --no-build-isolation -e python/[test] - name: Run tests (serial) run: coverage run --rcfile=.coveragerc -m mpi4py -m pytest python/tests/ -vs diff --git a/docker/Dockerfile b/docker/Dockerfile index 3329540f..7f30d700 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -3,17 +3,17 @@ FROM ghcr.io/fenics/dolfinx/dolfinx:nightly WORKDIR /tmp # This argument should be the same as what-ever the python version of the dol -ARG PYTHON_VERSION=3.10 +ARG PYTHON_VERSION=3.12 # Set env variables ENV HDF5_MPI="ON" \ HDF5_DIR="/usr/local" -RUN python3 -m pip install -U pip setuptools +RUN python3 -m pip install --break-system-packages -U pip setuptools # Install h5py https://github.com/h5py/h5py/issues/2222 -RUN python3 -m pip install --no-cache-dir --no-binary=h5py git+https://github.com/h5py/h5py.git -RUN python3 -m pip install meshio +RUN python3 -m pip install --break-system-packages --no-cache-dir --no-binary=h5py git+https://github.com/h5py/h5py.git +RUN python3 -m pip install --break-system-packages meshio # Copy DOLFINX_MPC source dir COPY . dolfinx_mpc @@ -23,7 +23,8 @@ RUN . /usr/local/bin/dolfinx-real-mode && \ . /usr/local/dolfinx-real/lib/dolfinx/dolfinx.conf && \ cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr/local/dolfinx-real -DCMAKE_BUILD_TYPE=Developer -B build-dir-real dolfinx_mpc/cpp/ && \ ninja install -j4 -C build-dir-real && \ - python3 -m pip install -v --no-build-isolation --check-build-dependencies --no-dependencies --target /usr/local/dolfinx-real/lib/python${PYTHON_VERSION}/dist-packages --config-settings=cmake.build-type=Developer -e dolfinx_mpc/python/[test] + python3 -m pip install -v --break-system-packages --no-build-isolation --check-build-dependencies \ + --target /usr/local/dolfinx-real/lib/python${PYTHON_VERSION}/dist-packages --no-dependencies --no-cache-dir ./dolfinx_mpc/python # Clean repo to remove build dir from pip RUN rm -rf dolfinx_mpc/python/build @@ -33,6 +34,7 @@ RUN . /usr/local/bin/dolfinx-complex-mode && \ . /usr/local/dolfinx-complex/lib/dolfinx/dolfinx.conf && \ cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr/local/dolfinx-complex -DCMAKE_BUILD_TYPE=Developer -B build-dir-complex dolfinx_mpc/cpp/ && \ ninja install -j4 -C build-dir-complex && \ - python3 -m pip install -v --no-build-isolation --check-build-dependencies --no-dependencies --target /usr/local/dolfinx-complex/lib/python${PYTHON_VERSION}/dist-packages --config-settings=cmake.build-type=Developer -e dolfinx_mpc/python/[test] + python3 -m pip install --break-system-packages -v --no-build-isolation --check-build-dependencies \ + --target /usr/local/dolfinx-complex/lib/python${PYTHON_VERSION}/dist-packages --no-dependencies --no-cache-dir ./dolfinx_mpc/python WORKDIR /root diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index e61b726c..87a33291 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -59,7 +59,8 @@ target_link_libraries(cpp PRIVATE dolfinx_mpc) # Check for petsc4py execute_process( - COMMAND ${Python_EXECUTABLE} -c "import petsc4py; print(petsc4py.get_include())" + COMMAND ${Python_EXECUTABLE} -c + "import petsc4py; print(petsc4py.get_include())" OUTPUT_VARIABLE PETSC4PY_INCLUDE_DIR RESULT_VARIABLE PETSC4PY_COMMAND_RESULT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE @@ -68,8 +69,9 @@ execute_process( if(NOT PETSC4PY_COMMAND_RESULT) message(STATUS "Found petsc4py include directory at ${PETSC4PY_INCLUDE_DIR}") target_include_directories(cpp PRIVATE ${PETSC4PY_INCLUDE_DIR}) + target_compile_definitions(cpp PRIVATE HAS_PETSC4PY) else() - message(FATAL_ERROR "petsc4py could not be found.") + message(FATAL_ERROR "petsc4py not found.") endif() diff --git a/python/benchmarks/bench_contact_3D.py b/python/benchmarks/bench_contact_3D.py index d5769da6..d9e36e95 100644 --- a/python/benchmarks/bench_contact_3D.py +++ b/python/benchmarks/bench_contact_3D.py @@ -211,7 +211,9 @@ def demo_stacked_cubes(theta, ct, noslip, num_refinements, N0, timings=False): mesh.name = f"mesh_{celltype}_{theta:.2f}{type_ext:s}" # Create functionspaces - el = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 1, shape=(mesh.geometry.dim,)) + el = basix.ufl.element( + "Lagrange", mesh.topology.cell_name(), 1, shape=(mesh.geometry.dim,), dtype=default_real_type + ) V = functionspace(mesh, el) # Define boundary conditions diff --git a/python/benchmarks/bench_elasticity.py b/python/benchmarks/bench_elasticity.py index 1024be95..aedbc39e 100644 --- a/python/benchmarks/bench_elasticity.py +++ b/python/benchmarks/bench_elasticity.py @@ -16,7 +16,7 @@ import basix.ufl import h5py import numpy as np -from dolfinx import default_scalar_type +from dolfinx import default_real_type, default_scalar_type from dolfinx.common import Timer, TimingType, list_timings from dolfinx.fem import ( Constant, @@ -49,7 +49,9 @@ def bench_elasticity_one( mesh = refine(mesh, redistribute=True) fdim = mesh.topology.dim - 1 - el = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 1, shape=(mesh.geometry.dim,)) + el = basix.ufl.element( + "Lagrange", mesh.topology.cell_name(), 1, shape=(mesh.geometry.dim,), dtype=default_real_type + ) V = functionspace(mesh, el) # Generate Dirichlet BC on lower boundary (Fixed) diff --git a/python/benchmarks/bench_elasticity_edge.py b/python/benchmarks/bench_elasticity_edge.py index 9ca1366d..237f6890 100644 --- a/python/benchmarks/bench_elasticity_edge.py +++ b/python/benchmarks/bench_elasticity_edge.py @@ -15,7 +15,7 @@ import basix.ufl import h5py import numpy as np -from dolfinx import default_scalar_type +from dolfinx import default_real_type, default_scalar_type from dolfinx.common import Timer, TimingType, list_timings from dolfinx.fem import ( Constant, @@ -62,7 +62,9 @@ def bench_elasticity_edge( ct = CellType.tetrahedron if tetra else CellType.hexahedron mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N, ct) - el = basix.ufl.element("Lagrange", mesh.topology.cell_name(), int(degree), shape=(mesh.geometry.dim,)) + el = basix.ufl.element( + "Lagrange", mesh.topology.cell_name(), int(degree), shape=(mesh.geometry.dim,), dtype=default_real_type + ) V = functionspace(mesh, el) # Generate Dirichlet BC (Fixed) diff --git a/python/benchmarks/ref_elasticity.py b/python/benchmarks/ref_elasticity.py index 9b464c79..c1eee62c 100644 --- a/python/benchmarks/ref_elasticity.py +++ b/python/benchmarks/ref_elasticity.py @@ -17,7 +17,7 @@ import basix.ufl import h5py import numpy as np -from dolfinx import default_scalar_type +from dolfinx import default_real_type, default_scalar_type from dolfinx.common import Timer, TimingType, list_timings from dolfinx.fem import ( Constant, @@ -73,7 +73,9 @@ def ref_elasticity( # set_log_level(LogLevel.ERROR) N = degree * N fdim = mesh.topology.dim - 1 - el = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 1, shape=(mesh.geometry.dim,)) + el = basix.ufl.element( + "Lagrange", mesh.topology.cell_name(), 1, shape=(mesh.geometry.dim,), dtype=default_real_type + ) V = functionspace(mesh, el) # Generate Dirichlet BC on lower boundary (Fixed) diff --git a/python/demos/demo_elasticity_disconnect.py b/python/demos/demo_elasticity_disconnect.py index dce1b7a6..b2edcab0 100644 --- a/python/demos/demo_elasticity_disconnect.py +++ b/python/demos/demo_elasticity_disconnect.py @@ -14,7 +14,7 @@ import basix.ufl import gmsh import numpy as np -from dolfinx import default_scalar_type +from dolfinx import default_real_type, default_scalar_type from dolfinx.fem import Constant, Function, dirichletbc, functionspace, locate_dofs_topological from dolfinx.io import XDMFFile, gmshio from ufl import ( @@ -224,6 +224,7 @@ def sigma(v): mesh.geometry.cmap.degree, lagrange_variant=basix.LagrangeVariant(mesh.geometry.cmap.variant), shape=(V.dofmap.bs,), + dtype=default_real_type, ), ) u_out = Function(V_out) diff --git a/python/demos/demo_stokes.py b/python/demos/demo_stokes.py index 1bf91873..d9753eff 100644 --- a/python/demos/demo_stokes.py +++ b/python/demos/demo_stokes.py @@ -21,7 +21,7 @@ import gmsh import numpy as np import scipy.sparse.linalg -from dolfinx import common, default_scalar_type, fem, io +from dolfinx import common, default_real_type, default_scalar_type, fem, io from dolfinx.io import gmshio from numpy.typing import NDArray from ufl import ( @@ -141,8 +141,8 @@ def create_mesh_gmsh( # The next step is the create the function spaces for the fluid velocit and pressure. # We will use a mixed-formulation, and we use `basix.ufl` to create the Taylor-Hood finite element pair -P2 = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 2, shape=(mesh.geometry.dim,)) -P1 = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 1) +P2 = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 2, shape=(mesh.geometry.dim,), dtype=default_real_type) +P1 = basix.ufl.element("Lagrange", mesh.topology.cell_name(), 1, dtype=default_real_type) TH = basix.ufl.mixed_element([P2, P1]) W = fem.functionspace(mesh, TH) diff --git a/python/demos/demo_stokes_nest.py b/python/demos/demo_stokes_nest.py index 35ff53a1..34732559 100644 --- a/python/demos/demo_stokes_nest.py +++ b/python/demos/demo_stokes_nest.py @@ -22,7 +22,7 @@ import numpy as np import scipy.sparse.linalg import ufl -from dolfinx import default_scalar_type +from dolfinx import default_real_type, default_scalar_type from dolfinx.io import gmshio from ufl.core.expr import Expr @@ -120,8 +120,8 @@ def create_mesh_gmsh( # Create the function space cellname = mesh.ufl_cell().cellname() -Ve = basix.ufl.element(basix.ElementFamily.P, cellname, 2, shape=(mesh.geometry.dim,)) -Qe = basix.ufl.element(basix.ElementFamily.P, cellname, 1) +Ve = basix.ufl.element(basix.ElementFamily.P, cellname, 2, shape=(mesh.geometry.dim,), dtype=default_real_type) +Qe = basix.ufl.element(basix.ElementFamily.P, cellname, 1, dtype=default_real_type) V = dolfinx.fem.functionspace(mesh, Ve) Q = dolfinx.fem.functionspace(mesh, Qe) diff --git a/python/dolfinx_mpc/mpc.cpp b/python/dolfinx_mpc/mpc.cpp index 5942caf4..30a73b9b 100644 --- a/python/dolfinx_mpc/mpc.cpp +++ b/python/dolfinx_mpc/mpc.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include namespace nb = nanobind; diff --git a/python/dolfinx_mpc/utils/mpc_utils.py b/python/dolfinx_mpc/utils/mpc_utils.py index 594a2010..9bf1689c 100644 --- a/python/dolfinx_mpc/utils/mpc_utils.py +++ b/python/dolfinx_mpc/utils/mpc_utils.py @@ -5,8 +5,6 @@ # SPDX-License-Identifier: MIT from __future__ import annotations -from contextlib import ExitStack - from mpi4py import MPI from petsc4py import PETSc @@ -176,36 +174,43 @@ def rigid_motions_nullspace(V: _fem.FunctionSpace): dim = 3 if gdim == 2 else 6 # Create list of vectors for null space - nullspace_basis = [_x.vector.copy() for _ in range(dim)] - - with ExitStack() as stack: - vec_local = [stack.enter_context(x.localForm()) for x in nullspace_basis] - basis = [np.asarray(x) for x in vec_local] - - dofs = [V.sub(i).dofmap.list.reshape(-1) for i in range(gdim)] - - # Build translational null space basis - for i in range(gdim): - basis[i][dofs[i]] = 1.0 - # Build rotational null space basis - x = V.tabulate_dof_coordinates() - dofs_block = V.dofmap.list.reshape(-1) - x0, x1, x2 = x[dofs_block, 0], x[dofs_block, 1], x[dofs_block, 2] - if gdim == 2: - basis[2][dofs[0]] = -x1 - basis[2][dofs[1]] = x0 - elif gdim == 3: - basis[3][dofs[0]] = -x1 - basis[3][dofs[1]] = x0 - - basis[4][dofs[0]] = x2 - basis[4][dofs[2]] = -x0 - basis[5][dofs[2]] = x1 - basis[5][dofs[1]] = -x2 + nullspace_basis = [ + _la.vector(V.dofmap.index_map, bs=V.dofmap.index_map_bs, dtype=PETSc.ScalarType) # type: ignore + for i in range(dim) + ] + + basis = [b.array for b in nullspace_basis] + dofs = [V.sub(i).dofmap.list.reshape(-1) for i in range(gdim)] + + # Build translational null space basis + for i in range(gdim): + basis[i][dofs[i]] = 1.0 + # Build rotational null space basis + x = V.tabulate_dof_coordinates() + dofs_block = V.dofmap.list.reshape(-1) + x0, x1, x2 = x[dofs_block, 0], x[dofs_block, 1], x[dofs_block, 2] + if gdim == 2: + basis[2][dofs[0]] = -x1 + basis[2][dofs[1]] = x0 + elif gdim == 3: + basis[3][dofs[0]] = -x1 + basis[3][dofs[1]] = x0 + + basis[4][dofs[0]] = x2 + basis[4][dofs[2]] = -x0 + basis[5][dofs[2]] = x1 + basis[5][dofs[1]] = -x2 + for b in nullspace_basis: + b.scatter_forward() _la.orthonormalize(nullspace_basis) - assert _la.is_orthonormal(nullspace_basis, float(500 * np.finfo(_x.x.array.dtype).resolution)) - return PETSc.NullSpace().create(vectors=nullspace_basis) # type: ignore + assert _la.is_orthonormal(nullspace_basis, np.finfo(_x.x.array.dtype).eps) + local_size = V.dofmap.index_map.size_local * V.dofmap.index_map_bs + basis_petsc = [ + PETSc.Vec().createWithArray(x[:local_size], bsize=gdim, comm=V.mesh.comm) # type: ignore + for x in basis + ] + return PETSc.NullSpace().create(comm=V.mesh.comm, vectors=basis_petsc) # type: ignore def determine_closest_block(V, point): diff --git a/python/pyproject.toml b/python/pyproject.toml index bd58332e..b4c66854 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ "cffi", "petsc4py", "mpi4py", - "fenics-dolfinx>=0.8.0.dev0,<0.9.0", + "fenics-dolfinx>0.8.0", ] [project.optional-dependencies] diff --git a/python/tests/test_nonlinear_assembly.py b/python/tests/test_nonlinear_assembly.py index 5885f7ad..9ae2d5c7 100644 --- a/python/tests/test_nonlinear_assembly.py +++ b/python/tests/test_nonlinear_assembly.py @@ -199,7 +199,7 @@ def test_homogenize(tensor_order, poly_order): pytest.xfail("Unknown tensor order") cellname = mesh.ufl_cell().cellname() - el = basix.ufl.element(basix.ElementFamily.P, cellname, poly_order, shape=shape) + el = basix.ufl.element(basix.ElementFamily.P, cellname, poly_order, shape=shape, dtype=mesh.geometry.x.dtype) V = dolfinx.fem.functionspace(mesh, el) diff --git a/python/tests/test_rectangular_assembly.py b/python/tests/test_rectangular_assembly.py index afee480d..4d84ad5a 100644 --- a/python/tests/test_rectangular_assembly.py +++ b/python/tests/test_rectangular_assembly.py @@ -48,15 +48,19 @@ def test_mixed_element(cell_type, ghost_mode): # Create the function space cellname = mesh.ufl_cell().cellname() - Ve = basix.ufl.element(basix.ElementFamily.P, cellname, 2, shape=(mesh.geometry.dim,)) - Qe = basix.ufl.element(basix.ElementFamily.P, cellname, 1) + Ve = basix.ufl.element( + basix.ElementFamily.P, cellname, 2, shape=(mesh.geometry.dim,), dtype=dolfinx.default_real_type + ) + Qe = basix.ufl.element(basix.ElementFamily.P, cellname, 1, dtype=dolfinx.default_real_type) V = dolfinx.fem.functionspace(mesh, Ve) Q = dolfinx.fem.functionspace(mesh, Qe) W = dolfinx.fem.functionspace(mesh, basix.ufl.mixed_element([Ve, Qe])) inlet_velocity = dolfinx.fem.Function(V) - inlet_velocity.interpolate(lambda x: np.zeros((mesh.geometry.dim, x[0].shape[0]), dtype=np.double)) + inlet_velocity.interpolate( + lambda x: np.zeros((mesh.geometry.dim, x[0].shape[0]), dtype=dolfinx.default_scalar_type) + ) inlet_velocity.x.scatter_forward() # -- Nested assembly