diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6b6c5529..a710ad12 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,103 +11,60 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: - test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + test-sequential: + name: Sequential - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} - env: - P4EST_ROOT_DIR: "/opt/p4est/2.8.5/" - JULIA_PETSC_LIBRARY: "/opt/petsc/3.18/lib/libpetsc" strategy: fail-fast: false matrix: version: - - '1.9' + - '1.10' os: - ubuntu-latest arch: - x64 steps: - uses: actions/checkout@v4 - - name: Cache p4est - id: cache-p4est - uses: actions/cache@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v2 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v5 with: - path: ${{env.P4EST_ROOT_DIR}} - key: ${{ runner.os }}-build-${{ env.P4EST_ROOT_DIR }}- - restore-keys: | - ${{ runner.os }}-build-${{ env.P4EST_ROOT_DIR }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + file: lcov.info + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} + test-mpi: + name: MPI - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.10' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - name: Install p4est/petsc dependencies - run: | - sudo apt-get update - sudo apt-get install -y wget gfortran g++ openmpi-bin libopenmpi-dev - - name: Install p4est - if: steps.cache-p4est.outputs.cache-hit != 'true' - run: | - # Install p4est 2.8.5 from sources - CURR_DIR=$(pwd) - PACKAGE=p4est - VERSION=2.8.5 - INSTALL_ROOT=/opt - P4EST_INSTALL=$INSTALL_ROOT/$PACKAGE/$VERSION - TAR_FILE=$PACKAGE-$VERSION.tar.gz - URL="https://github.com/p4est/p4est.github.io/raw/master/release" - ROOT_DIR=/tmp - SOURCES_DIR=$ROOT_DIR/$PACKAGE-$VERSION - BUILD_DIR=$SOURCES_DIR/build - wget -q $URL/$TAR_FILE -O $ROOT_DIR/$TAR_FILE - mkdir -p $SOURCES_DIR - tar xzf $ROOT_DIR/$TAR_FILE -C $SOURCES_DIR --strip-components=1 - cd $SOURCES_DIR - ./configure --prefix=$P4EST_INSTALL --without-blas --without-lapack --enable-mpi -disable-dependency-tracking - make --quiet - make --quiet install - rm -rf $ROOT_DIR/$TAR_FILE $SOURCES_DIR - cd $CURR_DIR - - name: Install petsc - ##if: steps.cache-petsc.outputs.cache-hit != 'true' - run: | - CURR_DIR=$(pwd) - PACKAGE=petsc - VERSION=3.18 - INSTALL_ROOT=/opt - PETSC_INSTALL=$INSTALL_ROOT/$PACKAGE/$VERSION - TAR_FILE=$PACKAGE-$VERSION.tar.gz - URL="https://ftp.mcs.anl.gov/pub/petsc/release-snapshots/" - ROOT_DIR=/tmp - SOURCES_DIR=$ROOT_DIR/$PACKAGE-$VERSION - BUILD_DIR=$SOURCES_DIR/build - wget -q $URL/$TAR_FILE -O $ROOT_DIR/$TAR_FILE - mkdir -p $SOURCES_DIR - tar xzf $ROOT_DIR/$TAR_FILE -C $SOURCES_DIR --strip-components=1 - cd $SOURCES_DIR - ./configure --prefix=$PETSC_INSTALL --with-cc=mpicc --with-cxx=mpicxx --with-fc=mpif90 \ - --download-mumps --download-scalapack --download-parmetis --download-metis \ - --download-fblaslapack --download-ptscotch --with-debugging --with-x=0 --with-shared=1 \ - --with-mpi=1 --with-64-bit-indices - make - make install - - name: add MPIPreferences - shell: julia --color=yes --project=. {0} - run: | - using Pkg - Pkg.add("MPIPreferences") - - name: use MPI system binary - shell: julia --color=yes --project=. {0} - run: | - using MPIPreferences - MPIPreferences.use_system_binary() - - uses: julia-actions/julia-buildpkg@latest - - run: echo $PWD - - run: julia --project=. -e 'using Pkg; Pkg.instantiate(); Pkg.build(); Pkg.precompile()' - - run: julia --project=. --color=yes --check-bounds=yes test/runtests.jl + - uses: julia-actions/cache@v2 + - uses: julia-actions/julia-buildpkg@v1 + - run: julia --project=. --color=yes -e 'using Pkg; Pkg.add("IterativeSolvers")' + - run: julia --project=. --color=yes test/runtests_mpi.jl + - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v5 with: + file: lcov.info + verbose: true token: ${{ secrets.CODECOV_TOKEN }} docs: name: Documentation diff --git a/.github/workflows/CI_libs.yml b/.github/workflows/CI_libs.yml new file mode 100644 index 00000000..a19282a4 --- /dev/null +++ b/.github/workflows/CI_libs.yml @@ -0,0 +1,103 @@ +name: CI-External-Libraries +on: + push: + branches: + - main + tags: ['*'] + pull_request: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: External Libraries - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + env: + P4EST_ROOT_DIR: "/opt/p4est/2.8.5/" + JULIA_PETSC_LIBRARY: "/opt/petsc/3.22.4/lib/libpetsc" + strategy: + fail-fast: false + matrix: + version: + - '1.10' + os: + - ubuntu-latest + arch: + - x64 + steps: + - name: Install p4est/petsc dependencies + run: | + sudo apt-get update + sudo apt-get install -y wget gfortran g++ openmpi-bin libopenmpi-dev + - name: Install p4est + run: | + # Install p4est 2.8.5 from sources + CURR_DIR=$(pwd) + PACKAGE=p4est + VERSION=2.8.5 + INSTALL_ROOT=/opt + P4EST_INSTALL=$INSTALL_ROOT/$PACKAGE/$VERSION + TAR_FILE=$PACKAGE-$VERSION.tar.gz + URL="https://github.com/p4est/p4est.github.io/raw/master/release" + ROOT_DIR=/tmp + SOURCES_DIR=$ROOT_DIR/$PACKAGE-$VERSION + BUILD_DIR=$SOURCES_DIR/build + wget -q $URL/$TAR_FILE -O $ROOT_DIR/$TAR_FILE + mkdir -p $SOURCES_DIR + tar xzf $ROOT_DIR/$TAR_FILE -C $SOURCES_DIR --strip-components=1 + cd $SOURCES_DIR + ./configure --prefix=$P4EST_INSTALL --without-blas --without-lapack --enable-mpi -disable-dependency-tracking + make --quiet + make --quiet install + rm -rf $ROOT_DIR/$TAR_FILE $SOURCES_DIR + cd $CURR_DIR + - name: Install petsc + run: | + CURR_DIR=$(pwd) + PACKAGE=petsc + VERSION=3.22.4 + INSTALL_ROOT=/opt + PETSC_INSTALL=$INSTALL_ROOT/$PACKAGE/$VERSION + TAR_FILE=$PACKAGE-$VERSION.tar.gz + URL="https://web.cels.anl.gov/projects/petsc/download/release-snapshots/" + ROOT_DIR=/tmp + SOURCES_DIR=$ROOT_DIR/$PACKAGE-$VERSION + BUILD_DIR=$SOURCES_DIR/build + wget -q $URL/$TAR_FILE -O $ROOT_DIR/$TAR_FILE + mkdir -p $SOURCES_DIR + tar xzf $ROOT_DIR/$TAR_FILE -C $SOURCES_DIR --strip-components=1 + cd $SOURCES_DIR + ./configure --prefix=$PETSC_INSTALL --with-cc=mpicc --with-cxx=mpicxx --with-fc=mpif90 \ + --download-mumps --download-scalapack --download-parmetis --download-metis \ + --download-fblaslapack --download-ptscotch --with-debugging --with-x=0 --with-shared=1 \ + --with-mpi=1 --with-64-bit-indices + make + make install + cd $CURR_DIR + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v2 + - name: add MPIPreferences and external packages + shell: julia --color=yes --project=. {0} + run: | + using Pkg + Pkg.add("MPIPreferences") + Pkg.add("GridapP4est") + Pkg.add("GridapPETSc") + - name: use MPI system binary + shell: julia --color=yes --project=. {0} + run: | + using MPIPreferences + MPIPreferences.use_system_binary() + - uses: julia-actions/julia-buildpkg@latest + - run: echo $PWD + - run: ls . + - run: ls test + - run: ls test/ExtLibraries + - run: julia --project=. -e 'using Pkg; Pkg.instantiate(); Pkg.build(); Pkg.precompile()' + - run: julia --project=. --color=yes test/ExtLibraries/runtests.jl diff --git a/NEWS.md b/NEWS.md index c8a15e81..32fbec4b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `RichardsonLinearSolver`. Since PR[#87](https://github.com/gridap/GridapSolvers.jl/pull/87). - Added `NullspaceSolver` for serial. Since PR[#88](https://github.com/gridap/GridapSolvers.jl/pull/88). +### Changed + +- **BREAKING:** Moved GridapP4est, GridapPETSc and IterativeSolvers into extensions (i.e weak dependencies). Since PR[#76](https://github.com/gridap/GridapSolvers.jl/pull/76). + ## Previous versions A changelog is not maintained for older versions than 0.4.0. diff --git a/Project.toml b/Project.toml index e64b78ea..36329ca4 100644 --- a/Project.toml +++ b/Project.toml @@ -9,9 +9,6 @@ BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" Gridap = "56d4f2e9-7ea1-5844-9cf6-b9c51ca7ce8e" GridapDistributed = "f9701e48-63b3-45aa-9a63-9bc6c271f355" -GridapP4est = "c2c8e14b-f5fd-423d-9666-1dd9ad120af9" -GridapPETSc = "bcdc36c2-0c3e-11ea-095a-c9dadae499f1" -IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" @@ -21,6 +18,16 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1" +[weakdeps] +GridapP4est = "c2c8e14b-f5fd-423d-9666-1dd9ad120af9" +GridapPETSc = "bcdc36c2-0c3e-11ea-095a-c9dadae499f1" +IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" + +[extensions] +GridapP4estExt = "GridapP4est" +GridapPETScExt = "GridapPETSc" +IterativeSolversExt = "IterativeSolvers" + [compat] AbstractTrees = "0.4" BlockArrays = "1" @@ -35,11 +42,11 @@ MPI = "0.20" NLsolve = "4.3.0" PartitionedArrays = "0.3" SparseMatricesCSR = "0.6.7" -julia = "1.7" +julia = "1.9" [extras] MPIPreferences = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test"] +test = ["Test","IterativeSolvers"] diff --git a/docs/Project.toml b/docs/Project.toml index 099f084e..fd3507ea 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,7 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +GridapP4est = "c2c8e14b-f5fd-423d-9666-1dd9ad120af9" +GridapPETSc = "bcdc36c2-0c3e-11ea-095a-c9dadae499f1" GridapSolvers = "6d3209ee-5e3c-4db7-a716-942eb12ed534" +IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" diff --git a/docs/examples.jl b/docs/examples.jl index f70d8cec..c8f0ba8d 100644 --- a/docs/examples.jl +++ b/docs/examples.jl @@ -14,6 +14,7 @@ for file in readdir(src_dir) out_file = string(out_dir,file) Literate.markdown( - in_file, out_dir + in_file, out_dir; + codefence = "````julia " => "````" ) end diff --git a/docs/make.jl b/docs/make.jl index 1d421a7c..e78b2243 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,22 +1,28 @@ using GridapSolvers +using GridapPETSc, GridapP4est, IterativeSolvers using Documenter include("examples.jl") +extensions = map( + ext -> Base.get_extension(GridapSolvers,ext), + (:GridapP4estExt,:GridapPETScExt,:IterativeSolversExt) +) + DocMeta.setdocmeta!(GridapSolvers, :DocTestSetup, :(using GridapSolvers); recursive=true) makedocs(; - modules=[GridapSolvers,GridapSolvers.BlockSolvers], - authors="Santiago Badia , Jordi Manyer , Alberto F. Martin ", - repo="https://github.com/gridap/GridapSolvers.jl/blob/{commit}{path}#{line}", - sitename="GridapSolvers.jl", - format=Documenter.HTML(; + modules = [GridapSolvers,extensions...], + authors = "Santiago Badia , Jordi Manyer , Alberto F. Martin ", + repo = "https://github.com/gridap/GridapSolvers.jl/blob/{commit}{path}#{line}", + sitename = "GridapSolvers.jl", + format = Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", canonical="https://gridap.github.io/GridapSolvers.jl", edit_link="main", assets=String[], ), - pages=[ + pages = [ "Home" => "index.md", "SolverInterfaces" => "SolverInterfaces.md", "MultilevelTools" => "MultilevelTools.md", @@ -24,6 +30,11 @@ makedocs(; "NonlinearSolvers" => "NonlinearSolvers.md", "BlockSolvers" => "BlockSolvers.md", "PatchBasedSmoothers" => "PatchBasedSmoothers.md", + "Extensions" => [ + "GridapP4est.jl" => "Extensions/GridapP4est.md", + "GridapPETSc.jl" => "Extensions/GridapPETSc.md", + "IterativeSolvers.jl" => "Extensions/IterativeSolvers.md", + ], "Examples" => [ "Stokes" => "Examples/Stokes.md", "Navier-Stokes" => "Examples/NavierStokes.md", @@ -32,7 +43,8 @@ makedocs(; "Darcy (GMG)" => "Examples/DarcyGMG.md", ], ], - warnonly=[:doctest,:example_block,:eval_block] + warnonly=[:doctest,:example_block,:eval_block], + clean=true, ) deploydocs(; diff --git a/docs/src/BlockSolvers.md b/docs/src/BlockSolvers.md index bd3598e8..e5ed44b0 100644 --- a/docs/src/BlockSolvers.md +++ b/docs/src/BlockSolvers.md @@ -1,10 +1,9 @@ +# GridapSolvers.BlockSolvers ```@meta CurrentModule = GridapSolvers.BlockSolvers ``` -# GridapSolvers.BlockSolvers - Many scalable preconditioners for multiphysics problems are based on (possibly partial) block factorizations. This module provides a simple interface to define and use block solvers for block-assembled systems. ## Block types diff --git a/docs/src/Extensions/GridapP4est.md b/docs/src/Extensions/GridapP4est.md new file mode 100644 index 00000000..ac3457a2 --- /dev/null +++ b/docs/src/Extensions/GridapP4est.md @@ -0,0 +1,37 @@ +# GridapP4est.jl extension + +```@meta +CurrentModule = Base.get_extension(GridapSolvers,:GridapP4estExt) +``` + +Building on top of [GridapP4est.jl](https://github.com/gridap/GridapP4est.jl), GridapSolvers provides tools to use the P4est library to build `DiscreteModelHierarchy` objects. + +```@docs +P4estCartesianModelHierarchy +``` + +## Examples of usage + +```julia +# Start from a coarse mesh, then refine +function p4est_mesh_by_refinement(distribute,np_per_level,domain,nc;nrefs=0) + GridapP4est.with(parts) do + parts = distribute(LinearIndices((np_per_level[1],))) + cparts = generate_subparts(parts,np_per_level[end]) + base = CartesianDiscreteModel(domain,nc) + cmodel = OctreeDistributedDiscreteModel(cparts,base,nrefs) + return ModelHierarchy(parts,cmodel,np_per_level) + end +end + +# Start from a fine mesh, then coarsen +function p4est_mesh_by_coarsening(distribute,np_per_level,domain,nc;nrefs=0) + GridapP4est.with(parts) do + n_levs = length(np_per_level) + fparts = distribute(LinearIndices((np_per_level[1],))) + base = CartesianDiscreteModel(domain,nc) + fmodel = OctreeDistributedDiscreteModel(fparts,base,nrefs+n_levs) + return ModelHierarchy(parts,fmodel,np_per_level) + end +end +``` diff --git a/docs/src/Extensions/GridapPETSc.md b/docs/src/Extensions/GridapPETSc.md new file mode 100644 index 00000000..82205840 --- /dev/null +++ b/docs/src/Extensions/GridapPETSc.md @@ -0,0 +1,13 @@ +# GridapPETSc.jl extension + +```@meta +CurrentModule = Base.get_extension(GridapSolvers,:GridapPETScExt) +``` + +Building on top of [GridapPETSc.jl](https://github.com/gridap/GridapPETSc.jl), GridapSolvers provides specific solvers for some particularly complex PDEs: + +```@docs + PETScElasticitySolver + PETScElasticitySolver(::FESpace) + CachedPETScNS +``` diff --git a/docs/src/Extensions/IterativeSolvers.md b/docs/src/Extensions/IterativeSolvers.md new file mode 100644 index 00000000..e8caa15d --- /dev/null +++ b/docs/src/Extensions/IterativeSolvers.md @@ -0,0 +1,15 @@ +# IterativeSolvers.jl extension + +```@meta +CurrentModule = Base.get_extension(GridapSolvers,:IterativeSolversExt) +``` + +GridapSolvers provides wrappers for some iterative solvers from the package [IterativeSolvers.jl](https://iterativesolvers.julialinearalgebra.org/dev/): + +```@docs + IterativeLinearSolver + IS_ConjugateGradientSolver + IS_GMRESSolver + IS_MINRESSolver + IS_SSORSolver +``` diff --git a/docs/src/Extensions/Pardiso.md b/docs/src/Extensions/Pardiso.md new file mode 100644 index 00000000..c8463dae --- /dev/null +++ b/docs/src/Extensions/Pardiso.md @@ -0,0 +1,5 @@ +# Pardiso.jl extension + +```@meta +CurrentModule = GridapSolvers +``` diff --git a/docs/src/LinearSolvers.md b/docs/src/LinearSolvers.md index 982001c0..92813beb 100644 --- a/docs/src/LinearSolvers.md +++ b/docs/src/LinearSolvers.md @@ -1,10 +1,9 @@ +# GridapSolvers.LinearSolvers ```@meta CurrentModule = GridapSolvers.LinearSolvers ``` -# GridapSolvers.LinearSolvers - ## Krylov solvers ```@docs @@ -54,34 +53,9 @@ Given a linear system ``Ax = b``, a **preconditioner** is an operator that takes GMGLinearSolver ``` -## Nullspaces +## Other ```@docs NullspaceSolver -``` - -## Wrappers - -### PETSc - -Building on top of [GridapPETSc.jl](https://github.com/gridap/GridapPETSc.jl), GridapSolvers provides specific solvers for some particularly complex PDEs: - -```@docs - ElasticitySolver - ElasticitySolver(::FESpace) - CachedPETScNS - CachedPETScNS(::GridapPETSc.PETScLinearSolverNS,::AbstractVector,::AbstractVector) - get_dof_coordinates -``` - -### IterativeSolvers.jl - -GridapSolvers provides wrappers for some iterative solvers from the package [IterativeSolvers.jl](https://iterativesolvers.julialinearalgebra.org/dev/): - -```@docs - IterativeLinearSolver - IS_ConjugateGradientSolver - IS_GMRESSolver - IS_MINRESSolver - IS_SSORSolver + CallbackSolver ``` diff --git a/docs/src/MultilevelTools.md b/docs/src/MultilevelTools.md index 45d61596..e8082cff 100644 --- a/docs/src/MultilevelTools.md +++ b/docs/src/MultilevelTools.md @@ -1,10 +1,9 @@ +# GridapSolvers.MultilevelTools ```@meta CurrentModule = GridapSolvers.MultilevelTools ``` -# GridapSolvers.MultilevelTools - ## Nested subpartitions One of the main difficulties of multilevel algorithms is dealing with the complexity of having multiple subcommunicators. We provide some tools to deal with it. In particular we introduce `HierarchicalArray`s. @@ -24,7 +23,6 @@ This objects are the multilevel counterparts of Gridap's `DiscreteModel` and `FE ModelHierarchy ModelHierarchyLevel CartesianModelHierarchy -P4estCartesianModelHierarchy FESpaceHierarchy ``` diff --git a/docs/src/index.md b/docs/src/index.md index d79076ed..2eedad55 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,9 +1,9 @@ +# GridapSolvers + ```@meta CurrentModule = GridapSolvers ``` -# GridapSolvers - Documentation for [GridapSolvers](https://github.com/gridap/GridapSolvers.jl). GridapSolvers provides algebraic and non-algebraic solvers for the Gridap ecosystem, designed with High Performance Computing (HPC) in mind. @@ -19,17 +19,31 @@ Pages = [ "LinearSolvers.md", "NonlinearSolvers.md", "BlockSolvers.md", - "PatchBasedSmoothers.md" - ] + "PatchBasedSmoothers.md", +] ``` -## Documentation and examples +### Extensions -A (hopefully) comprehensive documentation is available [here](https://gridap.github.io/GridapSolvers.jl/stable/). +```@contents +Pages = [ + "Extensions/GridapP4est.md", + "Extensions/GridapPETSc.md", + "Extensions/IterativeSolvers.md", +] +``` -A list of examples is available in the documentation. These include some very well known examples such as the Stokes, Incompressible Navier-Stokes and Darcy problems. The featured scripts are available in `test/Applications`. +### Examples -An example on how to use the library within an HPC cluster is available in `joss_paper/scalability`. The included example and drivers are used to generate the scalability results in our [JOSS paper](https://doi.org/10.21105/joss.07162). +```@contents +Pages = [ + "Examples/Stokes.md", + "Examples/NavierStokes.md", + "Examples/StokesGMG.md", + "Examples/NavierStokesGMG.md", + "Examples/DarcyGMG.md", +] +``` ## Installation @@ -40,12 +54,29 @@ pkg> add GridapSolvers pkg> build ``` -Building is required to link the external artifacts (e.g., PETSc, p4est) to the Julia environment. Restarting Julia is required after building in order to make the changes take effect. +If using the extensions for `GridapP4est.jl` or `GridapPETSc.jl`, building is required to link the external artifacts (e.g., PETSc, p4est) to the Julia environment. Restarting Julia is required after building in order to make the changes take effect. -### Using custom binaries - -The previous installations steps will setup GridapSolvers to work using Julia's pre-compiled artifacts for MPI, PETSc and p4est. However, you can also link local copies of these libraries. This might be very desirable in clusters, where hardware-specific libraries might be faster/more stable than the ones provided by Julia. To do so, follow the next steps: +By default, Julia will configure `GridapSolvers` to work using Julia's pre-compiled artifacts for MPI, PETSc and p4est. However, you can also link local copies of these libraries. This might be very desirable in clusters, where hardware-specific libraries might be faster/more stable than the ones provided by Julia. To do so, follow the next steps: - [MPI.jl](https://juliaparallel.org/MPI.jl/stable/configuration/) - [GridapPETSc.jl](https://github.com/gridap/GridapPETSc.jl) - [GridapP4est.jl](https://github.com/gridap/GridapP4est.jl), and [P4est_wrapper.jl](https://github.com/gridap/p4est_wrapper.jl) + +## Citation + +In order to give credit to the `GridapSolvers` contributors, we simply ask you to cite the `Gridap` main project as indicated [here](https://github.com/gridap/Gridap.jl#how-to-cite-gridap) and the sub-packages you use as indicated in the corresponding repositories. Please, use the reference below in any publication in which you have made use of `GridapSolvers`: + +```latex +@article{Manyer2024, + doi = {10.21105/joss.07162}, + url = {https://doi.org/10.21105/joss.07162}, + year = {2024}, + publisher = {The Open Journal}, + volume = {9}, + number = {102}, + pages = {7162}, + author = {Jordi Manyer and Alberto F. Martín and Santiago Badia}, + title = {GridapSolvers.jl: Scalable multiphysics finite element solvers in Julia}, + journal = {Journal of Open Source Software} +} +``` diff --git a/ext/GridapP4estExt/GridapP4estExt.jl b/ext/GridapP4estExt/GridapP4estExt.jl new file mode 100644 index 00000000..c83f8701 --- /dev/null +++ b/ext/GridapP4estExt/GridapP4estExt.jl @@ -0,0 +1,61 @@ +module GridapP4estExt + +using Gridap +using GridapDistributed +using PartitionedArrays +using GridapSolvers + +using GridapSolvers.MultilevelTools + +using GridapP4est + +""" + P4estCartesianModelHierarchy( + ranks,np_per_level,domain,nc; + num_refs_coarse::Integer = 0, + add_labels!::Function = (labels -> nothing), + map::Function = identity, + isperiodic = Tuple(fill(false,length(nc))) + ) + + Returns a `ModelHierarchy` with a Cartesian model as coarsest level, using GridapP4est.jl. + The i-th level will be distributed among `np_per_level[i]` processors. + The seed model is given by `cmodel = CartesianDiscreteModel(domain,nc)`. +""" +function GridapSolvers.P4estCartesianModelHierarchy( + ranks,np_per_level,domain,nc; + num_refs_coarse = 0, + add_labels! = (labels -> nothing), + map = identity, + isperiodic = Tuple(fill(false,length(nc))) +) + cparts = generate_subparts(ranks,np_per_level[end]) + cmodel = CartesianDiscreteModel(domain,nc;map,isperiodic) + add_labels!(get_face_labeling(cmodel)) + + coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,num_refs_coarse) + mh = ModelHierarchy(ranks,coarse_model,np_per_level) + return mh +end + +function GridapDistributed.DistributedAdaptedDiscreteModel( + model :: GridapP4est.OctreeDistributedDiscreteModel, + parent :: GridapDistributed.DistributedDiscreteModel, + glue :: AbstractArray{<:Gridap.Adaptivity.AdaptivityGlue}; +) + GridapDistributed.DistributedAdaptedDiscreteModel( + model.dmodel,parent,glue + ) +end + +function GridapDistributed.DistributedAdaptedDiscreteModel( + model :: GridapP4est.OctreeDistributedDiscreteModel, + parent :: GridapP4est.OctreeDistributedDiscreteModel, + glue :: AbstractArray{<:Gridap.Adaptivity.AdaptivityGlue}; +) + GridapDistributed.DistributedAdaptedDiscreteModel( + model.dmodel,parent.dmodel,glue + ) +end + +end # module \ No newline at end of file diff --git a/src/LinearSolvers/PETSc/ElasticitySolvers.jl b/ext/GridapPETScExt/ElasticitySolvers.jl similarity index 64% rename from src/LinearSolvers/PETSc/ElasticitySolvers.jl rename to ext/GridapPETScExt/ElasticitySolvers.jl index 4968bca5..1c6f1f97 100644 --- a/src/LinearSolvers/PETSc/ElasticitySolvers.jl +++ b/ext/GridapPETScExt/ElasticitySolvers.jl @@ -1,38 +1,30 @@ -""" - struct ElasticitySolver <: LinearSolver - ... - end - - GMRES + AMG solver, specifically designed for linear elasticity problems. - Follows PETSc's documentation for [PCAMG](https://petsc.org/release/manualpages/PC/PCGAMG.html) - and [MatNullSpaceCreateRigidBody](https://petsc.org/release/manualpages/Mat/MatNullSpaceCreateRigidBody.html). -""" -struct ElasticitySolver{A} <: Algebra.LinearSolver +struct _PETScElasticitySolver{A} <: Algebra.LinearSolver space :: A tols :: SolverTolerances{Float64} +end - @doc """ - function ElasticitySolver(space::FESpace; maxiter=500, atol=1.e-12, rtol=1.e-8) +""" + PETScElasticitySolver(space::FESpace; maxiter=500, atol=1.e-12, rtol=1.e-8) - Returns an instance of [`ElasticitySolver`](@ref) from its underlying properties. - """ - function ElasticitySolver(space::FESpace; - maxiter=500,atol=1.e-12,rtol=1.e-8) - tols = SolverTolerances{Float64}(;maxiter=maxiter,atol=atol,rtol=rtol) - A = typeof(space) - new{A}(space,tols) - end +GMRES + AMG solver, specifically designed for linear elasticity problems. + +Follows PETSc's documentation for [PCAMG](https://petsc.org/release/manualpages/PC/PCGAMG.html) +and [MatNullSpaceCreateRigidBody](https://petsc.org/release/manualpages/Mat/MatNullSpaceCreateRigidBody.html). +""" +function GridapSolvers.PETScElasticitySolver(space::FESpace; maxiter=500, atol=1.e-12, rtol=1.e-8) + tols = SolverTolerances{Float64}(;maxiter=maxiter,atol=atol,rtol=rtol) + _PETScElasticitySolver(space,tols) end -SolverInterfaces.get_solver_tolerances(s::ElasticitySolver) = s.tols +SolverInterfaces.get_solver_tolerances(s::_PETScElasticitySolver) = s.tols -struct ElasticitySymbolicSetup{A} <: SymbolicSetup +struct PETScElasticitySymbolicSetup{A} <: SymbolicSetup solver::A end -function Gridap.Algebra.symbolic_setup(solver::ElasticitySolver,A::AbstractMatrix) - ElasticitySymbolicSetup(solver) +function Gridap.Algebra.symbolic_setup(solver::_PETScElasticitySolver,A::AbstractMatrix) + PETScElasticitySymbolicSetup(solver) end function elasticity_ksp_setup(ksp,tols) @@ -51,28 +43,28 @@ function elasticity_ksp_setup(ksp,tols) @check_error_code GridapPETSc.PETSC.KSPView(ksp[],C_NULL) end -mutable struct ElasticityNumericalSetup <: NumericalSetup +mutable struct PETScElasticityNumericalSetup <: NumericalSetup A::PETScMatrix X::PETScVector B::PETScVector ksp::Ref{GridapPETSc.PETSC.KSP} null::Ref{GridapPETSc.PETSC.MatNullSpace} initialized::Bool - function ElasticityNumericalSetup(A::PETScMatrix,X::PETScVector,B::PETScVector) + function PETScElasticityNumericalSetup(A::PETScMatrix,X::PETScVector,B::PETScVector) ksp = Ref{GridapPETSc.PETSC.KSP}() null = Ref{GridapPETSc.PETSC.MatNullSpace}() new(A,X,B,ksp,null,false) end end -function GridapPETSc.Init(a::ElasticityNumericalSetup) +function GridapPETSc.Init(a::PETScElasticityNumericalSetup) @assert Threads.threadid() == 1 GridapPETSc._NREFS[] += 2 a.initialized = true finalizer(GridapPETSc.Finalize,a) end -function GridapPETSc.Finalize(ns::ElasticityNumericalSetup) +function GridapPETSc.Finalize(ns::PETScElasticityNumericalSetup) if ns.initialized && GridapPETSc.Initialized() if ns.A.comm == MPI.COMM_SELF @check_error_code GridapPETSc.PETSC.KSPDestroy(ns.ksp) @@ -88,7 +80,7 @@ function GridapPETSc.Finalize(ns::ElasticityNumericalSetup) nothing end -function Gridap.Algebra.numerical_setup(ss::ElasticitySymbolicSetup,_A::PSparseMatrix) +function Gridap.Algebra.numerical_setup(ss::PETScElasticitySymbolicSetup,_A::PSparseMatrix) _num_dims(space::FESpace) = num_cell_dims(get_triangulation(space)) _num_dims(space::GridapDistributed.DistributedSingleFieldFESpace) = getany(map(_num_dims,local_views(space))) s = ss.solver @@ -97,7 +89,7 @@ function Gridap.Algebra.numerical_setup(ss::ElasticitySymbolicSetup,_A::PSparseM A = convert(PETScMatrix,_A) X = convert(PETScVector,allocate_in_domain(_A)) B = convert(PETScVector,allocate_in_domain(_A)) - ns = ElasticityNumericalSetup(A,X,B) + ns = PETScElasticityNumericalSetup(A,X,B) # Compute coordinates for owned dofs dof_coords = convert(PETScVector,get_dof_coordinates(s.space)) @@ -115,7 +107,7 @@ function Gridap.Algebra.numerical_setup(ss::ElasticitySymbolicSetup,_A::PSparseM GridapPETSc.Init(ns) end -function Gridap.Algebra.numerical_setup!(ns::ElasticityNumericalSetup,A::AbstractMatrix) +function Gridap.Algebra.numerical_setup!(ns::PETScElasticityNumericalSetup,A::AbstractMatrix) ns.A = convert(PETScMatrix,A) @check_error_code GridapPETSc.PETSC.MatSetNearNullSpace(ns.A.mat[],ns.null[]) @check_error_code GridapPETSc.PETSC.KSPSetOperators(ns.ksp[],ns.A.mat[],ns.A.mat[]) @@ -123,7 +115,7 @@ function Gridap.Algebra.numerical_setup!(ns::ElasticityNumericalSetup,A::Abstrac ns end -function Algebra.solve!(x::AbstractVector{PetscScalar},ns::ElasticityNumericalSetup,b::AbstractVector{PetscScalar}) +function Algebra.solve!(x::AbstractVector{PetscScalar},ns::PETScElasticityNumericalSetup,b::AbstractVector{PetscScalar}) X, B = ns.X, ns.B copy!(B,b) @check_error_code GridapPETSc.PETSC.KSPSolve(ns.ksp[],B.vec[],X.vec[]) diff --git a/ext/GridapPETScExt/GridapPETScExt.jl b/ext/GridapPETScExt/GridapPETScExt.jl new file mode 100644 index 00000000..436f47ce --- /dev/null +++ b/ext/GridapPETScExt/GridapPETScExt.jl @@ -0,0 +1,30 @@ +module GridapPETScExt + +using MPI +using LinearAlgebra +using SparseArrays +using SparseMatricesCSR + +using Gridap +using Gridap.Helpers, Gridap.Algebra, Gridap.CellData +using Gridap.Arrays, Gridap.FESpaces, Gridap.MultiField +using Gridap.Fields, Gridap.ReferenceFEs + +using GridapDistributed +using PartitionedArrays +using GridapPETSc + +using GridapSolvers +using GridapSolvers.MultilevelTools +using GridapSolvers.SolverInterfaces +using GridapSolvers.LinearSolvers +using GridapSolvers.PatchBasedSmoothers + +using PartitionedArrays: getany + +include("PETScUtils.jl") +include("PETScCaches.jl") +include("ElasticitySolvers.jl") +include("HipmairXuSolvers.jl") + +end # module \ No newline at end of file diff --git a/src/LinearSolvers/PETSc/HipmairXuSolvers.jl b/ext/GridapPETScExt/HipmairXuSolvers.jl similarity index 100% rename from src/LinearSolvers/PETSc/HipmairXuSolvers.jl rename to ext/GridapPETScExt/HipmairXuSolvers.jl diff --git a/ext/GridapPETScExt/PETScCaches.jl b/ext/GridapPETScExt/PETScCaches.jl new file mode 100644 index 00000000..012d34ca --- /dev/null +++ b/ext/GridapPETScExt/PETScCaches.jl @@ -0,0 +1,79 @@ + +struct _CachedPETScNS{TM,A} + ns :: GridapPETSc.PETScLinearSolverNS{TM} + X :: PETScVector + B :: PETScVector + owners :: A +end + +""" + CachedPETScNS(ns::PETScLinearSolverNS,x::AbstractVector,b::AbstractVector) + +Wrapper around a PETSc NumericalSetup, providing highly efficiend reusable caches: + +When converting julia vectors/PVectors to PETSc vectors, we purposely create aliasing +of the vector values. This means we can avoid copying data from one to another before solving, +but we need to be careful about it. + +This structure takes care of this, and makes sure you do not attempt to solve the system +with julia vectors that are not the ones you used to create the solver cache. +Once this structure is created, you can **only** solve the system with the same vectors +you used to create it. +""" +function GridapSolvers.CachedPETScNS(ns::GridapPETSc.PETScLinearSolverNS,x::AbstractVector,b::AbstractVector) + X = convert(PETScVector,x) + B = convert(PETScVector,b) + owners = (x,b) + _CachedPETScNS(ns,X,B,owners) +end + +function Algebra.solve!(x::AbstractVector,ns::_CachedPETScNS,b::AbstractVector) + @assert x === ns.owners[1] + @assert b === ns.owners[2] + solve!(ns.X,ns.ns,ns.B) + consistent!(x) + return x +end + +function Algebra.numerical_setup!(ns::_CachedPETScNS,mat::AbstractMatrix) + numerical_setup!(ns.ns,mat) +end + +function Algebra.numerical_setup!(ns::_CachedPETScNS,mat::AbstractMatrix,x::AbstractVector) + numerical_setup!(ns.ns,mat,x) +end + +############################################################################################ +# Optimisations for GridapSolvers + PETSc + +function LinearSolvers.gmg_coarse_solver_caches( + solver::PETScLinearSolver, + smatrices::AbstractVector{<:AbstractMatrix}, + work_vectors +) + nlevs = num_levels(smatrices) + with_level(smatrices,nlevs) do AH + _, _, dxH, rH = work_vectors[nlevs-1] + cache = CachedPETScNS( + numerical_setup(symbolic_setup(solver, AH), AH), dxH, rH + ) + return cache + end +end + +function LinearSolvers.gmg_coarse_solver_caches( + solver::PETScLinearSolver, + smatrices::AbstractVector{<:AbstractMatrix}, + svectors::AbstractVector{<:AbstractVector}, + work_vectors +) + nlevs = num_levels(smatrices) + with_level(smatrices,nlevs) do AH + _, _, dxH, rH = work_vectors[nlevs-1] + xH = svectors[nlevs] + cache = CachedPETScNS( + numerical_setup(symbolic_setup(solver, AH, xH), AH, xH), dxH,rH + ) + return cache + end +end diff --git a/src/LinearSolvers/PETSc/PETScUtils.jl b/ext/GridapPETScExt/PETScUtils.jl similarity index 94% rename from src/LinearSolvers/PETSc/PETScUtils.jl rename to ext/GridapPETScExt/PETScUtils.jl index 24464808..34b08eb6 100644 --- a/src/LinearSolvers/PETSc/PETScUtils.jl +++ b/ext/GridapPETScExt/PETScUtils.jl @@ -1,12 +1,12 @@ # DoF coordinates -""" - get_dof_coordinates(space::FESpace) - - Given a lagrangian FESpace, returns the physical coordinates of the DoFs, as required - by some PETSc solvers. See [PETSc documentation](https://petsc.org/release/manualpages/PC/PCSetCoordinates.html). -""" +# """ +# get_dof_coordinates(space::FESpace) +# +# Given a lagrangian FESpace, returns the physical coordinates of the DoFs, as required +# by some PETSc solvers. See [PETSc documentation](https://petsc.org/release/manualpages/PC/PCSetCoordinates.html). +# """ function get_dof_coordinates(space::GridapDistributed.DistributedSingleFieldFESpace) coords = map(local_views(space),partition(space.gids)) do space, dof_ids local_to_own_dofs = local_to_own(dof_ids) diff --git a/src/LinearSolvers/IterativeLinearSolvers.jl b/ext/IterativeSolversExt.jl similarity index 59% rename from src/LinearSolvers/IterativeLinearSolvers.jl rename to ext/IterativeSolversExt.jl index 22d13e6b..0391b88a 100644 --- a/src/LinearSolvers/IterativeLinearSolvers.jl +++ b/ext/IterativeSolversExt.jl @@ -1,3 +1,12 @@ +module IterativeSolversExt + +using LinearAlgebra, SparseArrays +using PartitionedArrays + +using Gridap, GridapSolvers +using IterativeSolvers + +using Gridap.Helpers, Gridap.Algebra abstract type IterativeLinearSolverType end struct CGIterativeSolverType <: IterativeLinearSolverType end @@ -24,7 +33,7 @@ struct SSORIterativeSolverType <: IterativeLinearSolverType end - [`IS_MINRESSolver`](@ref) - [`IS_SSORSolver`](@ref) """ -struct IterativeLinearSolver{A} <: Gridap.Algebra.LinearSolver +struct IterativeLinearSolver{A} <: Algebra.LinearSolver args kwargs @@ -41,9 +50,9 @@ SolverType(::IterativeLinearSolver{T}) where T = T() Wrapper for the [Conjugate Gradient solver](https://iterativesolvers.julialinearalgebra.org/dev/linear_systems/cg/). """ -function IS_ConjugateGradientSolver(;kwargs...) +function GridapSolvers.IS_ConjugateGradientSolver(;kwargs...) options = [:statevars,:initially_zero,:Pl,:abstol,:reltol,:maxiter,:verbose,:log] - @check all(map(opt -> opt ∈ options,keys(kwargs))) + @check all(map(opt -> opt ∈ options, keys(kwargs))) return IterativeLinearSolver(CGIterativeSolverType(),nothing,kwargs) end @@ -52,9 +61,9 @@ end Wrapper for the [GMRES solver](https://iterativesolvers.julialinearalgebra.org/dev/linear_systems/gmres/). """ -function IS_GMRESSolver(;kwargs...) +function GridapSolvers.IS_GMRESSolver(;kwargs...) options = [:initially_zero,:abstol,:reltol,:restart,:maxiter,:Pl,:Pr,:log,:verbose,:orth_meth] - @check all(map(opt -> opt ∈ options,keys(kwargs))) + @check all(map(opt -> opt ∈ options, keys(kwargs))) return IterativeLinearSolver(GMRESIterativeSolverType(),nothing,kwargs) end @@ -63,9 +72,9 @@ end Wrapper for the [MINRES solver](https://iterativesolvers.julialinearalgebra.org/dev/linear_systems/minres/). """ -function IS_MINRESSolver(;kwargs...) +function GridapSolvers.IS_MINRESSolver(;kwargs...) options = [:initially_zero,:skew_hermitian,:abstol,:reltol,:maxiter,:log,:verbose] - @check all(map(opt -> opt ∈ options,keys(kwargs))) + @check all(map(opt -> opt ∈ options, keys(kwargs))) return IterativeLinearSolver(MINRESIterativeSolverType(),nothing,kwargs) end @@ -74,16 +83,16 @@ end Wrapper for the [SSOR solver](https://iterativesolvers.julialinearalgebra.org/dev/linear_systems/stationary/#SSOR). """ -function IS_SSORSolver(ω::Real;kwargs...) +function GridapSolvers.IS_SSORSolver(ω::Real;kwargs...) options = [:maxiter] - @check all(map(opt -> opt ∈ options,keys(kwargs))) + @check all(map(opt -> opt ∈ options, keys(kwargs))) args = Dict(:ω => ω) return IterativeLinearSolver(SSORIterativeSolverType(),args,kwargs) end # Symbolic setup -struct IterativeLinearSolverSS <: Gridap.Algebra.SymbolicSetup +struct IterativeLinearSolverSS <: Algebra.SymbolicSetup solver end @@ -93,7 +102,7 @@ end # Numerical setup -struct IterativeLinearSolverNS <: Gridap.Algebra.NumericalSetup +struct IterativeLinearSolverNS <: Algebra.NumericalSetup solver A caches @@ -104,23 +113,29 @@ function Gridap.Algebra.numerical_setup(ss::IterativeLinearSolverSS,A::AbstractM numerical_setup(solver_type,ss,A) end -function Gridap.Algebra.numerical_setup(::IterativeLinearSolverType, - ss::IterativeLinearSolverSS, - A::AbstractMatrix) +function Gridap.Algebra.numerical_setup( + ::IterativeLinearSolverType, + ss::IterativeLinearSolverSS, + A::AbstractMatrix +) IterativeLinearSolverNS(ss.solver,A,nothing) end -function Gridap.Algebra.numerical_setup(::CGIterativeSolverType, - ss::IterativeLinearSolverSS, - A::AbstractMatrix) +function Gridap.Algebra.numerical_setup( + ::CGIterativeSolverType, + ss::IterativeLinearSolverSS, + A::AbstractMatrix +) x = allocate_in_domain(A); fill!(x,zero(eltype(x))) caches = IterativeSolvers.CGStateVariables(zero(x), similar(x), similar(x)) return IterativeLinearSolverNS(ss.solver,A,caches) end -function Gridap.Algebra.numerical_setup(::SSORIterativeSolverType, - ss::IterativeLinearSolverSS, - A::AbstractMatrix) +function Gridap.Algebra.numerical_setup( + ::SSORIterativeSolverType, + ss::IterativeLinearSolverSS, + A::AbstractMatrix +) x = allocate_in_range(A); fill!(x,zero(eltype(x))) b = allocate_in_domain(A); fill!(b,zero(eltype(b))) ω = ss.solver.args[:ω] @@ -129,11 +144,13 @@ function Gridap.Algebra.numerical_setup(::SSORIterativeSolverType, return IterativeLinearSolverNS(ss.solver,A,caches) end -function IterativeSolvers.ssor_iterable(x::PVector, - A::PSparseMatrix, - b::PVector, - ω::Real; - maxiter::Int = 10) +function IterativeSolvers.ssor_iterable( + x::PVector, + A::PSparseMatrix, + b::PVector, + ω::Real; + maxiter::Int = 10 +) iterables = map(own_values(x),own_values(A),own_values(b)) do _xi,_Aii,_bi xi = Vector(_xi) Aii = SparseMatrixCSC(_Aii) @@ -149,48 +166,60 @@ function LinearAlgebra.ldiv!(x::AbstractVector,ns::IterativeLinearSolverNS,b::Ab solve!(x,ns,b) end -function Gridap.Algebra.solve!(x::AbstractVector, - ns::IterativeLinearSolverNS, - y::AbstractVector) +function Algebra.solve!( + x::AbstractVector, + ns::IterativeLinearSolverNS, + y::AbstractVector +) solver_type = SolverType(ns.solver) solve!(solver_type,x,ns,y) end -function Gridap.Algebra.solve!(::IterativeLinearSolverType, - ::AbstractVector, - ::IterativeLinearSolverNS, - ::AbstractVector) +function Algebra.solve!( + ::IterativeLinearSolverType, + ::AbstractVector, + ::IterativeLinearSolverNS, + ::AbstractVector +) @abstractmethod end -function Gridap.Algebra.solve!(::CGIterativeSolverType, - x::AbstractVector, - ns::IterativeLinearSolverNS, - y::AbstractVector) +function Algebra.solve!( + ::CGIterativeSolverType, + x::AbstractVector, + ns::IterativeLinearSolverNS, + y::AbstractVector +) A, kwargs, caches = ns.A, ns.solver.kwargs, ns.caches return cg!(x,A,y;kwargs...,statevars=caches) end -function Gridap.Algebra.solve!(::GMRESIterativeSolverType, - x::AbstractVector, - ns::IterativeLinearSolverNS, - y::AbstractVector) +function Algebra.solve!( + ::GMRESIterativeSolverType, + x::AbstractVector, + ns::IterativeLinearSolverNS, + y::AbstractVector +) A, kwargs = ns.A, ns.solver.kwargs return gmres!(x,A,y;kwargs...) end -function Gridap.Algebra.solve!(::MINRESIterativeSolverType, - x::AbstractVector, - ns::IterativeLinearSolverNS, - y::AbstractVector) +function Algebra.solve!( + ::MINRESIterativeSolverType, + x::AbstractVector, + ns::IterativeLinearSolverNS, + y::AbstractVector +) A, kwargs = ns.A, ns.solver.kwargs return minres!(x,A,y;kwargs...) end -function Gridap.Algebra.solve!(::SSORIterativeSolverType, - x::AbstractVector, - ns::IterativeLinearSolverNS, - y::AbstractVector) +function Algebra.solve!( + ::SSORIterativeSolverType, + x::AbstractVector, + ns::IterativeLinearSolverNS, + y::AbstractVector +) iterable = ns.caches iterable.x = x iterable.b = y @@ -199,10 +228,12 @@ function Gridap.Algebra.solve!(::SSORIterativeSolverType, return x end -function Gridap.Algebra.solve!(::SSORIterativeSolverType, - x::PVector, - ns::IterativeLinearSolverNS, - y::PVector) +function Algebra.solve!( + ::SSORIterativeSolverType, + x::PVector, + ns::IterativeLinearSolverNS, + y::PVector +) iterables = ns.caches map(iterables,own_values(x),own_values(y)) do iterable, xi, yi iterable.x .= xi @@ -214,3 +245,5 @@ function Gridap.Algebra.solve!(::SSORIterativeSolverType, consistent!(x) |> fetch return x end + +end # module \ No newline at end of file diff --git a/src/BlockSolvers/BlockSolvers.jl b/src/BlockSolvers/BlockSolvers.jl index 23d29c89..613925a9 100644 --- a/src/BlockSolvers/BlockSolvers.jl +++ b/src/BlockSolvers/BlockSolvers.jl @@ -4,7 +4,6 @@ using LinearAlgebra using SparseArrays using SparseMatricesCSR using BlockArrays -using IterativeSolvers using Gridap using Gridap.Helpers, Gridap.Algebra, Gridap.CellData, Gridap.Arrays, Gridap.FESpaces, Gridap.MultiField diff --git a/src/GridapSolvers.jl b/src/GridapSolvers.jl index 1135ca96..752a940a 100644 --- a/src/GridapSolvers.jl +++ b/src/GridapSolvers.jl @@ -39,11 +39,6 @@ export SymGaussSeidelSmoother export GMGLinearSolver export BlockDiagonalSmoother -export ConjugateGradientSolver -export IS_GMRESSolver -export IS_MINRESSolver -export IS_SSORSolver - export CGSolver export MINRESSolver export GMRESSolver @@ -55,4 +50,6 @@ export PatchFESpace export PatchBasedLinearSolver export Closure +include("extensions.jl") + end diff --git a/src/LinearSolvers/CallbackSolver.jl b/src/LinearSolvers/CallbackSolver.jl index 87eca8d0..382c8b89 100644 --- a/src/LinearSolvers/CallbackSolver.jl +++ b/src/LinearSolvers/CallbackSolver.jl @@ -1,4 +1,18 @@ +""" + CallbackSolver(solver::LinearSolver,callback::Function) + +A linear solver that runs a callback function after solving the linear system. The callback +function should take the solution vector as its only argument and return nothing, i.e +`callback(x::AbstractVector) -> nothing`. + +This structure is useful to add functionality to any linear solver, such as: + +- Logging the solution, residuals, etc. +- Monitoring properties of the solution, as it's divergence or mean. +- Modifying the solution in-place after solving the linear system, to apply a correction, + for example. +""" struct CallbackSolver{A,B} <: Algebra.LinearSolver solver :: A callback :: B diff --git a/src/LinearSolvers/GMGLinearSolvers.jl b/src/LinearSolvers/GMGLinearSolvers.jl index b564e48b..554fc22e 100644 --- a/src/LinearSolvers/GMGLinearSolvers.jl +++ b/src/LinearSolvers/GMGLinearSolvers.jl @@ -126,7 +126,7 @@ function GMGLinearSolver( tols = SolverTolerances{Float64}(;maxiter=maxiter,atol=atol,rtol=rtol) log = ConvergenceLog("GMG",tols;verbose=verbose) - primal_restrictions = is_nonlinear ? setup_restriction_operators(trials,8;mode=:solution,solver=IS_ConjugateGradientSolver(;reltol=1.e-6)) : nothing + primal_restrictions = is_nonlinear ? setup_restriction_operators(trials,8;mode=:solution,solver=CGSolver(JacobiLinearSolver())) : nothing return GMGLinearSolverFromWeakform( mh,trials,tests,biforms,interp,restrict,pre_smoothers,post_smoothers,coarsest_solver,mode,log,is_nonlinear,primal_restrictions ) @@ -373,9 +373,6 @@ function gmg_coarse_solver_caches( with_level(smatrices,nlevs) do AH _, _, dxH, rH = work_vectors[nlevs-1] cache = numerical_setup(symbolic_setup(solver, AH), AH) - if isa(solver,PETScLinearSolver) - cache = CachedPETScNS(cache, dxH, rH) - end return cache end end @@ -391,9 +388,6 @@ function gmg_coarse_solver_caches( _, _, dxH, rH = work_vectors[nlevs-1] xH = svectors[nlevs] cache = numerical_setup(symbolic_setup(solver, AH, xH), AH, xH) - if isa(solver,PETScLinearSolver) - cache = CachedPETScNS(cache, dxH, rH) - end return cache end end diff --git a/src/LinearSolvers/LinearSolvers.jl b/src/LinearSolvers/LinearSolvers.jl index b52b46cd..fec47306 100644 --- a/src/LinearSolvers/LinearSolvers.jl +++ b/src/LinearSolvers/LinearSolvers.jl @@ -6,12 +6,10 @@ using LinearAlgebra using SparseArrays using SparseMatricesCSR using BlockArrays -using IterativeSolvers using Gridap using Gridap.Helpers, Gridap.Algebra, Gridap.CellData, Gridap.Arrays, Gridap.FESpaces, Gridap.MultiField using PartitionedArrays -using GridapPETSc using GridapDistributed using GridapSolvers.MultilevelTools @@ -49,11 +47,6 @@ include("Krylov/GMRESSolvers.jl") include("Krylov/FGMRESSolvers.jl") include("Krylov/MINRESSolvers.jl") -include("PETSc/PETScUtils.jl") -include("PETSc/PETScCaches.jl") -include("PETSc/ElasticitySolvers.jl") -include("PETSc/HipmairXuSolvers.jl") - include("IdentityLinearSolvers.jl") include("LinearSolverFromSmoothers.jl") @@ -63,7 +56,6 @@ include("SymGaussSeidelSmoothers.jl") include("RichardsonLinearSolvers.jl") include("GMGLinearSolvers.jl") -include("IterativeLinearSolvers.jl") include("SchurComplementSolvers.jl") include("MatrixSolvers.jl") include("SchwarzLinearSolvers.jl") diff --git a/src/LinearSolvers/PETSc/PETScCaches.jl b/src/LinearSolvers/PETSc/PETScCaches.jl deleted file mode 100644 index 9ba88d3b..00000000 --- a/src/LinearSolvers/PETSc/PETScCaches.jl +++ /dev/null @@ -1,53 +0,0 @@ - -""" - struct CachedPETScNS <: NumericalSetup - ... - end - - Wrapper around a PETSc NumericalSetup, providing highly efficiend reusable caches: - - When converting julia vectors/PVectors to PETSc vectors, we purposely create aliasing - of the vector values. This means we can avoid copying data from one to another before solving, - but we need to be careful about it. - - This structure takes care of this, and makes sure you do not attempt to solve the system - with julia vectors that are not the ones you used to create the solver cache. -""" -struct CachedPETScNS{TM,A} - ns :: GridapPETSc.PETScLinearSolverNS{TM} - X :: PETScVector - B :: PETScVector - owners :: A - - @doc """ - function CachedPETScNS(ns::PETScLinearSolverNS,x::AbstractVector,b::AbstractVector) - - Create a new instance of [`CachedPETScNS`](@ref) from its underlying properties. - Once this structure is created, you can **only** solve the system with the same vectors - you used to create it. - """ - function CachedPETScNS(ns::GridapPETSc.PETScLinearSolverNS{TM},x::AbstractVector,b::AbstractVector) where TM - X = convert(PETScVector,x) - B = convert(PETScVector,b) - owners = (x,b) - - A = typeof(owners) - new{TM,A}(ns,X,B,owners) - end -end - -function Algebra.solve!(x::AbstractVector,ns::CachedPETScNS,b::AbstractVector) - @assert x === ns.owners[1] - @assert b === ns.owners[2] - solve!(ns.X,ns.ns,ns.B) - consistent!(x) - return x -end - -function Algebra.numerical_setup!(ns::CachedPETScNS,mat::AbstractMatrix) - numerical_setup!(ns.ns,mat) -end - -function Algebra.numerical_setup!(ns::CachedPETScNS,mat::AbstractMatrix,x::AbstractVector) - numerical_setup!(ns.ns,mat,x) -end diff --git a/src/MultilevelTools/ModelHierarchies.jl b/src/MultilevelTools/ModelHierarchies.jl index 61147c3e..701b595e 100644 --- a/src/MultilevelTools/ModelHierarchies.jl +++ b/src/MultilevelTools/ModelHierarchies.jl @@ -128,7 +128,7 @@ function CartesianModelHierarchy( model_ref, ref_glue = nothing, nothing, nothing end # Redistribution (if needed) - if i_am_in(fparts) && (cparts !== fparts) + if i_am_in(fparts) && (cparts !== fparts) _model_ref = i_am_in(cparts) ? Gridap.Adaptivity.get_model(model_ref) : nothing model_red, red_glue = GridapDistributed.redistribute(_model_ref,level_descs[lev];old_ranks=cparts) else @@ -177,55 +177,6 @@ function ModelHierarchy(models::Vector{<:GridapDistributed.DistributedDiscreteMo return HierarchicalArray(meshes,level_parts) end -""" - P4estCartesianModelHierarchy( - ranks,np_per_level,domain,nc::NTuple{D,<:Integer}; - num_refs_coarse::Integer = 0, - add_labels!::Function = (labels -> nothing), - map::Function = identity, - isperiodic::NTuple{D,Bool} = Tuple(fill(false,D)) - ) where D - - Returns a `ModelHierarchy` with a Cartesian model as coarsest level, using GridapP4est.jl. - The i-th level will be distributed among `np_per_level[i]` processors. - The seed model is given by `cmodel = CartesianDiscreteModel(domain,nc)`. -""" -function P4estCartesianModelHierarchy( - ranks,np_per_level,domain,nc::NTuple{D,<:Integer}; - num_refs_coarse::Integer = 0, - add_labels!::Function = (labels -> nothing), - map::Function = identity, - isperiodic::NTuple{D,Bool} = Tuple(fill(false,D)) -) where D - cparts = generate_subparts(ranks,np_per_level[end]) - cmodel = CartesianDiscreteModel(domain,nc;map,isperiodic) - add_labels!(get_face_labeling(cmodel)) - - coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,num_refs_coarse) - mh = ModelHierarchy(ranks,coarse_model,np_per_level) - return mh -end - -function GridapDistributed.DistributedAdaptedDiscreteModel( - model :: GridapP4est.OctreeDistributedDiscreteModel, - parent :: GridapDistributed.DistributedDiscreteModel, - glue :: AbstractArray{<:Gridap.Adaptivity.AdaptivityGlue}; -) - GridapDistributed.DistributedAdaptedDiscreteModel( - model.dmodel,parent,glue - ) -end - -function GridapDistributed.DistributedAdaptedDiscreteModel( - model :: GridapP4est.OctreeDistributedDiscreteModel, - parent :: GridapP4est.OctreeDistributedDiscreteModel, - glue :: AbstractArray{<:Gridap.Adaptivity.AdaptivityGlue}; -) - GridapDistributed.DistributedAdaptedDiscreteModel( - model.dmodel,parent.dmodel,glue - ) -end - """ ModelHierarchy(root_parts,model,num_procs_x_level) diff --git a/src/MultilevelTools/MultilevelTools.jl b/src/MultilevelTools/MultilevelTools.jl index 4ef8c95a..ee18f0d8 100644 --- a/src/MultilevelTools/MultilevelTools.jl +++ b/src/MultilevelTools/MultilevelTools.jl @@ -9,7 +9,7 @@ using Gridap using Gridap.Helpers, Gridap.Algebra, Gridap.Arrays, Gridap.Fields, Gridap.CellData using Gridap.ReferenceFEs, Gridap.Geometry, Gridap.FESpaces, Gridap.Adaptivity, Gridap.MultiField -using PartitionedArrays, GridapDistributed, GridapP4est +using PartitionedArrays, GridapDistributed using Gridap.FESpaces: BasisStyle, TestBasis, TrialBasis, SingleFieldFEBasis using Gridap.MultiField: MultiFieldFEBasisComponent diff --git a/src/MultilevelTools/SubpartitioningTools.jl b/src/MultilevelTools/SubpartitioningTools.jl index ed4a39b9..343d1094 100644 --- a/src/MultilevelTools/SubpartitioningTools.jl +++ b/src/MultilevelTools/SubpartitioningTools.jl @@ -11,18 +11,15 @@ function generate_level_parts(root_parts::AbstractArray,num_procs_x_level::Vecto level_parts = Vector{T}(undef,num_levels) level_parts[1] = generate_subparts(root_parts,num_procs_x_level[1]) for l = 2:num_levels - level_parts[l] = generate_level_parts(root_parts,level_parts[l-1],num_procs_x_level[l]) + if num_procs_x_level[l] == num_procs_x_level[l-1] + level_parts[l] = level_parts[l-1] + else + level_parts[l] = generate_subparts(root_parts, num_procs_x_level[l]) + end end return level_parts end -function generate_level_parts(root_parts::AbstractArray,last_level_parts::AbstractArray,level_parts_size::Integer) - if level_parts_size == num_parts(last_level_parts) - return last_level_parts - end - return generate_subparts(root_parts,level_parts_size) -end - my_print(x::PVector,s) = my_print(partition(x),s) function my_print(x::MPIArray,s) diff --git a/src/extensions.jl b/src/extensions.jl new file mode 100644 index 00000000..7f87543b --- /dev/null +++ b/src/extensions.jl @@ -0,0 +1,28 @@ + +# This file contains placeholders for stuff that is exported from extensions. + +# IterativeSolversExt + +export IS_ConjugateGradientSolver +export IS_GMRESSolver +export IS_MINRESSolver +export IS_SSORSolver + +function IS_ConjugateGradientSolver end +function IS_GMRESSolver end +function IS_MINRESSolver end +function IS_SSORSolver end + +# GridapP4estExt + +export P4estCartesianModelHierarchy + +function P4estCartesianModelHierarchy end + +# GridapPETScExt + +export PETScElasticitySolver +export CachedPETScNS + +function PETScElasticitySolver end +function CachedPETScNS end diff --git a/test/Applications/DarcyGMG.jl b/test/Applications/DarcyGMG.jl index f3a64100..70fca530 100644 --- a/test/Applications/DarcyGMG.jl +++ b/test/Applications/DarcyGMG.jl @@ -85,7 +85,7 @@ function main(distribute,np,nc,np_per_level) tests_u,qdegree;mode=:residual ) restrictions = setup_restriction_operators( - tests_u,qdegree;mode=:residual,solver=IS_ConjugateGradientSolver(;reltol=1.e-6) + tests_u,qdegree;mode=:residual,solver=CGSolver(JacobiLinearSolver()) ) gmg = GMGLinearSolver( diff --git a/test/Applications/Elasticity.jl b/test/Applications/Elasticity.jl new file mode 100644 index 00000000..ffac91e7 --- /dev/null +++ b/test/Applications/Elasticity.jl @@ -0,0 +1,52 @@ + +using Gridap +using Gridap.Geometry, Gridap.Algebra + +using PartitionedArrays +using GridapDistributed +using GridapPETSc + +using GridapSolvers + +function main(distribute,np) + ranks = distribute(LinearIndices((prod(np),))) + model = CartesianDiscreteModel(ranks,np,(0,1,0,1),(20,20)) + + labels = get_face_labeling(model) + add_tag_from_tags!(labels,"diri_0",[1,3,7]) + add_tag_from_tags!(labels,"diri_1",[2,4,8]) + + order = 1 + reffe = ReferenceFE(lagrangian,VectorValue{2,Float64},order) + V = TestFESpace(model,reffe,dirichlet_tags=["diri_0", "diri_1"]) + + disp_x = 0.5 + g0 = VectorValue(0.0,0.0) + g1 = VectorValue(disp_x,0.0) + U = TrialFESpace(V,[g0,g1]) + + λ = 100.0 + μ = 1.0 + σ(ε) = λ*tr(ε)*one(ε) + 2*μ*ε + + degree = 2*order + Ω = Triangulation(model) + dΩ = Measure(Ω,degree) + + a(u,v) = ∫(ε(v) ⊙ (σ∘ε(u)))*dΩ + l(v) = 0 + + op = AffineFEOperator(a,l,U,V) + + options = "-ksp_error_if_not_converged true -ksp_converged_reason" + x = GridapPETSc.with(args=split(options)) do + solver = PETScElasticitySolver(U) + A, b = get_matrix(op), get_vector(op) + ns = numerical_setup(symbolic_setup(solver,A),A) + x = allocate_in_domain(A) + fill!(x,0.0) + solve!(x,ns,b) + end + + uh = FEFunction(U,x) +end diff --git a/test/Applications/NavierStokesGMG.jl b/test/Applications/NavierStokesGMG.jl index ffd0134e..69d42a01 100644 --- a/test/Applications/NavierStokesGMG.jl +++ b/test/Applications/NavierStokesGMG.jl @@ -34,7 +34,6 @@ using Gridap.ReferenceFEs, Gridap.Algebra, Gridap.Geometry, Gridap.FESpaces using Gridap.CellData, Gridap.MultiField, Gridap.Algebra using PartitionedArrays using GridapDistributed -using GridapP4est using GridapSolvers using GridapSolvers.LinearSolvers, GridapSolvers.MultilevelTools @@ -135,7 +134,7 @@ function main(distribute,np,nc,np_per_level) tests_u,jac_u,graddiv,qdegree;is_nonlinear=true ) restrictions = setup_patch_restriction_operators( - tests_u,prolongations,graddiv,qdegree;solver=IS_ConjugateGradientSolver(;reltol=1.e-6) + tests_u,prolongations,graddiv,qdegree;solver=CGSolver(JacobiLinearSolver()) ) gmg = GMGLinearSolver( mh,trials_u,tests_u,biforms, diff --git a/test/Applications/Stokes.jl b/test/Applications/Stokes.jl index 82690126..3bb84c57 100644 --- a/test/Applications/Stokes.jl +++ b/test/Applications/Stokes.jl @@ -90,7 +90,7 @@ function main(distribute,np,nc) A, b = get_matrix(op), get_vector(op); solver_u = LUSolver() - solver_p = CGSolver(JacobiLinearSolver();maxiter=20,atol=1e-14,rtol=1.e-6,verbose=i_am_main(parts)) + solver_p = CGSolver(JacobiLinearSolver();maxiter=20,atol=1e-14,rtol=1.e-6) solver_p.log.depth = 2 bblocks = [LinearSystemBlock() LinearSystemBlock(); diff --git a/test/Applications/StokesGMG.jl b/test/Applications/StokesGMG.jl index 2ad1e84c..38a5868d 100644 --- a/test/Applications/StokesGMG.jl +++ b/test/Applications/StokesGMG.jl @@ -34,7 +34,6 @@ using Gridap.ReferenceFEs, Gridap.Algebra, Gridap.Geometry, Gridap.FESpaces using Gridap.CellData, Gridap.MultiField, Gridap.Algebra using PartitionedArrays using GridapDistributed -using GridapP4est using GridapSolvers using GridapSolvers.LinearSolvers, GridapSolvers.MultilevelTools, GridapSolvers.PatchBasedSmoothers diff --git a/test/Applications/mpi/runtests.jl b/test/Applications/mpi/runtests.jl index ab8af694..300bea52 100644 --- a/test/Applications/mpi/runtests.jl +++ b/test/Applications/mpi/runtests.jl @@ -2,15 +2,18 @@ using Test using MPI using GridapSolvers -function run_tests(testdir) +function run_tests(testdir,procs=4) istest(f) = endswith(f, ".jl") && !(f=="runtests.jl") testfiles = sort(filter(istest, readdir(testdir))) @time @testset "$f" for f in testfiles MPI.mpiexec() do cmd - np = 4 - cmd = `$cmd -n $(np) --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))` - @show cmd - run(cmd) + if MPI.MPI_LIBRARY == "OpenMPI" || (isdefined(MPI, :OpenMPI) && MPI.MPI_LIBRARY == MPI.OpenMPI) + run(`$cmd -n $procs --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + else + run(`$cmd -n $procs $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + end + # This line will be reached if and only if the command launched by `run` runs without errors. + # Then, if we arrive here, the test has succeeded. @test true end end diff --git a/test/BlockSolvers/mpi/runtests.jl b/test/BlockSolvers/mpi/runtests.jl index b43e871a..6002b89b 100644 --- a/test/BlockSolvers/mpi/runtests.jl +++ b/test/BlockSolvers/mpi/runtests.jl @@ -2,15 +2,18 @@ using Test using MPI using GridapSolvers -function run_tests(testdir) +function run_tests(testdir,procs=4) istest(f) = endswith(f, ".jl") && !(f=="runtests.jl") testfiles = sort(filter(istest, readdir(testdir))) @time @testset "$f" for f in testfiles MPI.mpiexec() do cmd - np = 4 - cmd = `$cmd -n $(np) --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))` - @show cmd - run(cmd) + if MPI.MPI_LIBRARY == "OpenMPI" || (isdefined(MPI, :OpenMPI) && MPI.MPI_LIBRARY == MPI.OpenMPI) + run(`$cmd -n $procs --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + else + run(`$cmd -n $procs $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + end + # This line will be reached if and only if the command launched by `run` runs without errors. + # Then, if we arrive here, the test has succeeded. @test true end end diff --git a/test/ExtLibraries/GridapP4estExtTests.jl b/test/ExtLibraries/GridapP4estExtTests.jl new file mode 100644 index 00000000..42446a43 --- /dev/null +++ b/test/ExtLibraries/GridapP4estExtTests.jl @@ -0,0 +1,48 @@ +module GridapP4estExtTests + +using Test +using Gridap +using GridapDistributed +using PartitionedArrays +using GridapP4est + +using GridapSolvers +using GridapSolvers.MultilevelTools + +function test_mh(mh) + sol(x) = x[1] + x[2] + reffe = ReferenceFE(lagrangian,Float64,1) + tests = TestFESpace(mh,reffe,conformity=:H1) + trials = TrialFESpace(tests,sol) + @test true +end + +function main(distribute,np,np_per_level) + domain = (0,1,0,1) + parts = distribute(LinearIndices((prod(np),))) + GridapP4est.with(parts) do + # Start from coarse, refine models + num_levels = length(np_per_level) + cparts = generate_subparts(parts,np_per_level[num_levels]) + cmodel = CartesianDiscreteModel(domain,(2,2)) + coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,2) + mh = ModelHierarchy(parts,coarse_model,np_per_level) + test_mh(mh) + + # Start from fine, coarsen models + fparts = generate_subparts(parts,np_per_level[1]) + fmodel = CartesianDiscreteModel(domain,(2,2)) + fine_model = OctreeDistributedDiscreteModel(fparts,fmodel,8) + mh = ModelHierarchy(parts,fine_model,np_per_level) + test_mh(mh) + + # P4estCartesianModelHierarchy + mh = P4estCartesianModelHierarchy(parts,np_per_level,domain,(2,2)) + end +end + +with_mpi() do distribute + main(distribute,4,[4,2,2,1]) +end + +end \ No newline at end of file diff --git a/test/ExtLibraries/GridapPETScExtTests.jl b/test/ExtLibraries/GridapPETScExtTests.jl new file mode 100644 index 00000000..d3d8002f --- /dev/null +++ b/test/ExtLibraries/GridapPETScExtTests.jl @@ -0,0 +1,7 @@ +using PartitionedArrays + +include("../Applications/Elasticity.jl") + +with_mpi() do distribute + main(distribute,(2,2)) +end diff --git a/test/ExtLibraries/runtests.jl b/test/ExtLibraries/runtests.jl new file mode 100644 index 00000000..e5fb200c --- /dev/null +++ b/test/ExtLibraries/runtests.jl @@ -0,0 +1,22 @@ +using Test +using MPI +using GridapSolvers + +function run_tests(testdir,procs=4) + istest(f) = endswith(f, ".jl") && !(f=="runtests.jl") + testfiles = sort(filter(istest, readdir(testdir))) + @time @testset "$f" for f in testfiles + MPI.mpiexec() do cmd + if MPI.MPI_LIBRARY == "OpenMPI" || (isdefined(MPI, :OpenMPI) && MPI.MPI_LIBRARY == MPI.OpenMPI) + run(`$cmd -n $procs --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + else + run(`$cmd -n $procs $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + end + # This line will be reached if and only if the command launched by `run` runs without errors. + # Then, if we arrive here, the test has succeeded. + @test true + end + end +end + +run_tests(@__DIR__) \ No newline at end of file diff --git a/test/LinearSolvers/GMGTests.jl b/test/LinearSolvers/GMGTests.jl index a4193bbc..60dd13f2 100644 --- a/test/LinearSolvers/GMGTests.jl +++ b/test/LinearSolvers/GMGTests.jl @@ -3,14 +3,12 @@ module GMGTests using MPI using Test using LinearAlgebra -using IterativeSolvers using FillArrays using Gridap using Gridap.ReferenceFEs, Gridap.Algebra using PartitionedArrays using GridapDistributed -using GridapP4est using GridapSolvers using GridapSolvers.LinearSolvers @@ -50,7 +48,7 @@ function gmg_driver_from_mats(t,parts,mh,spaces,qdegree,smoothers,biform,liform, tests, trials = spaces restrictions, prolongations = setup_transfer_operators( - trials, qdegree; mode=:residual, solver=IS_ConjugateGradientSolver(;reltol=1.e-6) + trials, qdegree; mode=:residual, solver=CGSolver(JacobiLinearSolver()) ) smatrices, A, b = compute_hierarchy_matrices(trials,tests,biform,liform,qdegree) @@ -91,7 +89,7 @@ function gmg_driver_from_weakform(t,parts,mh,spaces,qdegree,smoothers,biform,lif tests, trials = spaces restrictions, prolongations = setup_transfer_operators( - trials, qdegree; mode=:residual, solver=IS_ConjugateGradientSolver(;reltol=1.e-6) + trials, qdegree; mode=:residual, solver=CGSolver(JacobiLinearSolver()) ) A, b = with_level(mh,1) do _ diff --git a/test/LinearSolvers/IterativeSolversWrappersTests.jl b/test/LinearSolvers/IterativeSolversExtTests.jl similarity index 98% rename from test/LinearSolvers/IterativeSolversWrappersTests.jl rename to test/LinearSolvers/IterativeSolversExtTests.jl index daf8edb2..0d25f22b 100644 --- a/test/LinearSolvers/IterativeSolversWrappersTests.jl +++ b/test/LinearSolvers/IterativeSolversExtTests.jl @@ -1,4 +1,4 @@ -module IterativeSolversWrappersTests +module IterativeSolversExtTests using Test using Gridap, Gridap.Algebra diff --git a/test/LinearSolvers/mpi/runtests.jl b/test/LinearSolvers/mpi/runtests.jl index b43e871a..6002b89b 100644 --- a/test/LinearSolvers/mpi/runtests.jl +++ b/test/LinearSolvers/mpi/runtests.jl @@ -2,15 +2,18 @@ using Test using MPI using GridapSolvers -function run_tests(testdir) +function run_tests(testdir,procs=4) istest(f) = endswith(f, ".jl") && !(f=="runtests.jl") testfiles = sort(filter(istest, readdir(testdir))) @time @testset "$f" for f in testfiles MPI.mpiexec() do cmd - np = 4 - cmd = `$cmd -n $(np) --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))` - @show cmd - run(cmd) + if MPI.MPI_LIBRARY == "OpenMPI" || (isdefined(MPI, :OpenMPI) && MPI.MPI_LIBRARY == MPI.OpenMPI) + run(`$cmd -n $procs --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + else + run(`$cmd -n $procs $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + end + # This line will be reached if and only if the command launched by `run` runs without errors. + # Then, if we arrive here, the test has succeeded. @test true end end diff --git a/test/LinearSolvers/seq/IterativeSolversExtTests.jl b/test/LinearSolvers/seq/IterativeSolversExtTests.jl new file mode 100644 index 00000000..476f96ee --- /dev/null +++ b/test/LinearSolvers/seq/IterativeSolversExtTests.jl @@ -0,0 +1,12 @@ +module IterativeSolversWrappersTestsSequential +using PartitionedArrays +include("../IterativeSolversExtTests.jl") + +with_debug() do distribute + IterativeSolversExtTests.main(distribute,(1,1)) # 2D - serial + IterativeSolversExtTests.main(distribute,(2,2)) # 2D + IterativeSolversExtTests.main(distribute,(1,1,1)) # 3D - serial + IterativeSolversExtTests.main(distribute,(2,2,1)) # 3D +end + +end \ No newline at end of file diff --git a/test/LinearSolvers/seq/IterativeSolversWrappersTests.jl b/test/LinearSolvers/seq/IterativeSolversWrappersTests.jl deleted file mode 100644 index 011d2153..00000000 --- a/test/LinearSolvers/seq/IterativeSolversWrappersTests.jl +++ /dev/null @@ -1,12 +0,0 @@ -module IterativeSolversWrappersTestsSequential -using PartitionedArrays -include("../IterativeSolversWrappersTests.jl") - -with_debug() do distribute - IterativeSolversWrappersTests.main(distribute,(1,1)) # 2D - serial - IterativeSolversWrappersTests.main(distribute,(2,2)) # 2D - IterativeSolversWrappersTests.main(distribute,(1,1,1)) # 3D - serial - IterativeSolversWrappersTests.main(distribute,(2,2,1)) # 3D -end - -end \ No newline at end of file diff --git a/test/LinearSolvers/seq/runtests.jl b/test/LinearSolvers/seq/runtests.jl index 72d1ff95..e9a50102 100644 --- a/test/LinearSolvers/seq/runtests.jl +++ b/test/LinearSolvers/seq/runtests.jl @@ -1,8 +1,7 @@ using Test include("KrylovTests.jl") -include("IterativeSolversWrappersTests.jl") +include("IterativeSolversExtTests.jl") include("SmoothersTests.jl") include("GMGTests.jl") include("RichardsonLinearTests.jl") - diff --git a/test/MultilevelTools/DistributedGridTransferOperatorsTests.jl b/test/MultilevelTools/DistributedGridTransferOperatorsTests.jl index c09be7da..40cd0da0 100644 --- a/test/MultilevelTools/DistributedGridTransferOperatorsTests.jl +++ b/test/MultilevelTools/DistributedGridTransferOperatorsTests.jl @@ -3,7 +3,6 @@ using MPI using PartitionedArrays using Gridap, Gridap.Algebra using GridapDistributed -using GridapP4est using Test using GridapSolvers @@ -11,23 +10,10 @@ using GridapSolvers.MultilevelTools using GridapDistributed: change_ghost -function get_model_hierarchy(parts,Dc,num_parts_x_level) - mh = GridapP4est.with(parts) do - if Dc == 2 - domain = (0,1,0,1) - nc = (2,2) - else - @assert Dc == 3 - domain = (0,1,0,1,0,1) - nc = (2,2,2) - end - num_refs_coarse = 2 - num_levels = length(num_parts_x_level) - cparts = generate_subparts(parts,num_parts_x_level[num_levels]) - cmodel = CartesianDiscreteModel(domain,nc) - coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,num_refs_coarse) - return ModelHierarchy(parts,coarse_model,num_parts_x_level) - end +function get_model_hierarchy(parts,Dc,np_per_level) + domain = ifelse(Dc == 2, (0,1,0,1), (0,1,0,1,0,1)) + nc = ifelse(Dc == 2, (4,4), (4,4,4)) + mh = CartesianModelHierarchy(parts,np_per_level,domain,nc) return mh end @@ -163,8 +149,4 @@ function main(distribute,np,Dc,np_x_level) end end -with_mpi() do distribute - main(distribute,4,2,[4,2,2]) -end - end # module DistributedGridTransferOperatorsTests \ No newline at end of file diff --git a/test/MultilevelTools/ModelHierarchiesTests.jl b/test/MultilevelTools/ModelHierarchiesTests.jl index 90cc3c2f..8afc8193 100644 --- a/test/MultilevelTools/ModelHierarchiesTests.jl +++ b/test/MultilevelTools/ModelHierarchiesTests.jl @@ -5,39 +5,16 @@ using Gridap using Gridap.FESpaces, Gridap.Algebra using GridapDistributed using PartitionedArrays -using GridapP4est using GridapSolvers using GridapSolvers.MultilevelTools -function main(distribute,np,num_parts_x_level) +function main(distribute,np,np_per_level) parts = distribute(LinearIndices((prod(np),))) - GridapP4est.with(parts) do - # Start from coarse, refine models - domain = (0,1,0,1) - num_levels = length(num_parts_x_level) - cparts = generate_subparts(parts,num_parts_x_level[num_levels]) - cmodel = CartesianDiscreteModel(domain,(2,2)) - coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,2) - mh = ModelHierarchy(parts,coarse_model,num_parts_x_level) - - sol(x) = x[1] + x[2] - reffe = ReferenceFE(lagrangian,Float64,1) - tests = TestFESpace(mh,reffe,conformity=:H1) - trials = TrialFESpace(tests,sol) - - # Start from fine, coarsen models - domain = (0,1,0,1) - fparts = generate_subparts(parts,num_parts_x_level[1]) - fmodel = CartesianDiscreteModel(domain,(2,2)) - fine_model = OctreeDistributedDiscreteModel(fparts,fmodel,8) - mh = ModelHierarchy(parts,fine_model,num_parts_x_level) - - sol(x) = x[1] + x[2] - reffe = ReferenceFE(lagrangian,Float64,1) - tests = TestFESpace(mh,reffe,conformity=:H1) - trials = TrialFESpace(tests,sol) - end + + domain = (0,1,0,1) + nc = (4,4) + mh = CartesianModelHierarchy(parts,np_per_level,domain,nc) end end \ No newline at end of file diff --git a/test/MultilevelTools/RedistributeToolsTests.jl b/test/MultilevelTools/RedistributeToolsTests.jl index bbb6031a..735877cc 100644 --- a/test/MultilevelTools/RedistributeToolsTests.jl +++ b/test/MultilevelTools/RedistributeToolsTests.jl @@ -3,30 +3,16 @@ using MPI using PartitionedArrays using Gridap, Gridap.Algebra using GridapDistributed -using GridapP4est using Test using GridapSolvers using GridapSolvers.MultilevelTools using GridapDistributed: redistribute_cell_dofs, redistribute_fe_function, redistribute_free_values -function get_model_hierarchy(parts,Dc,num_parts_x_level) - mh = GridapP4est.with(parts) do - if Dc == 2 - domain = (0,1,0,1) - nc = (2,2) - else - @assert Dc == 3 - domain = (0,1,0,1,0,1) - nc = (2,2,2) - end - num_refs_coarse = 2 - num_levels = length(num_parts_x_level) - cparts = generate_subparts(parts,num_parts_x_level[num_levels]) - cmodel = CartesianDiscreteModel(domain,nc) - coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,num_refs_coarse) - return ModelHierarchy(parts,coarse_model,num_parts_x_level) - end +function get_model_hierarchy(parts,Dc,np_per_level) + domain = ifelse(Dc == 2, (0,1,0,1), (0,1,0,1,0,1)) + nc = ifelse(Dc == 2, (4,4), (4,4,4)) + mh = CartesianModelHierarchy(parts,np_per_level,domain,nc) return mh end diff --git a/test/MultilevelTools/RefinementToolsTests.jl b/test/MultilevelTools/RefinementToolsTests.jl index d7399319..4411795c 100644 --- a/test/MultilevelTools/RefinementToolsTests.jl +++ b/test/MultilevelTools/RefinementToolsTests.jl @@ -3,30 +3,16 @@ using MPI using PartitionedArrays using Gridap, Gridap.Algebra using GridapDistributed -using GridapP4est using Test -using IterativeSolvers using GridapSolvers using GridapSolvers.MultilevelTools +using GridapSolvers.LinearSolvers -function get_model_hierarchy(parts,Dc,num_parts_x_level) - mh = GridapP4est.with(parts) do - if Dc == 2 - domain = (0,1,0,1) - nc = (2,2) - else - @assert Dc == 3 - domain = (0,1,0,1,0,1) - nc = (2,2,2) - end - num_refs_coarse = 2 - num_levels = length(num_parts_x_level) - cparts = generate_subparts(parts,num_parts_x_level[num_levels]) - cmodel = CartesianDiscreteModel(domain,nc) - coarse_model = OctreeDistributedDiscreteModel(cparts,cmodel,num_refs_coarse) - return ModelHierarchy(parts,coarse_model,num_parts_x_level) - end +function get_model_hierarchy(parts,Dc,np_per_level) + domain = ifelse(Dc == 2, (0,1,0,1), (0,1,0,1,0,1)) + nc = ifelse(Dc == 2, (4,4), (4,4,4)) + mh = CartesianModelHierarchy(parts,np_per_level,domain,nc) return mh end @@ -37,6 +23,7 @@ function main_driver(parts,mh) reffe = ReferenceFE(lagrangian,Float64,order) tests = TestFESpace(mh,reffe;conformity=:H1,dirichlet_tags="boundary") trials = TrialFESpace(tests,sol) + solver = CGSolver(JacobiLinearSolver();verbose=i_am_main(parts),rtol=1.0e-8) nlevs = num_levels(mh) quad_order = 2*order+1 @@ -68,7 +55,8 @@ function main_driver(parts,mh) bh = get_vector(oph) xh = pfill(0.0,partition(axes(Ah,2))) - IterativeSolvers.cg!(xh,Ah,bh;verbose=i_am_main(parts),reltol=1.0e-08) + ns = numerical_setup(symbolic_setup(solver,Ah),Ah) + solve!(xh,ns,bh) uH_projected = FEFunction(Uh,xh) _eh = uh-uH_projected @@ -84,7 +72,8 @@ function main_driver(parts,mh) bH = get_vector(opH) xH = pfill(0.0,partition(axes(AH,2))) - IterativeSolvers.cg!(xH,AH,bH;verbose=i_am_main(parts),reltol=1.0e-08) + ns = numerical_setup(symbolic_setup(solver,AH),AH) + solve!(xH,ns,bH) uh_projected = FEFunction(UH,xH) _eH = uH-uh_projected diff --git a/test/MultilevelTools/mpi/DistributedGridTransferOperatorsTests.jl b/test/MultilevelTools/mpi/DistributedGridTransferOperatorsTests.jl index 10458f1d..6341eee5 100644 --- a/test/MultilevelTools/mpi/DistributedGridTransferOperatorsTests.jl +++ b/test/MultilevelTools/mpi/DistributedGridTransferOperatorsTests.jl @@ -3,7 +3,7 @@ using MPI, PartitionedArrays include("../DistributedGridTransferOperatorsTests.jl") with_mpi() do distribute - DistributedGridTransferOperatorsTests.main(distribute,4,2,[4,2,2]) # 2D + DistributedGridTransferOperatorsTests.main(distribute,4,2,[(2,2),(2,1),(2,1)]) # 2D #DistributedGridTransferOperatorsTests.main(distribute,4,3,[4,2,2]) # 3D end diff --git a/test/MultilevelTools/mpi/ModelHierarchiesTests.jl b/test/MultilevelTools/mpi/ModelHierarchiesTests.jl index e3659346..3a967691 100644 --- a/test/MultilevelTools/mpi/ModelHierarchiesTests.jl +++ b/test/MultilevelTools/mpi/ModelHierarchiesTests.jl @@ -3,7 +3,7 @@ using MPI, PartitionedArrays include("../ModelHierarchiesTests.jl") with_mpi() do distribute - ModelHierarchiesTests.main(distribute,4,[4,4,2,2]) + ModelHierarchiesTests.main(distribute,4,[(2,2),(2,2),(2,1),(1,1)]) end end \ No newline at end of file diff --git a/test/MultilevelTools/mpi/RedistributeToolsTests.jl b/test/MultilevelTools/mpi/RedistributeToolsTests.jl index d86e3fb1..7b931206 100644 --- a/test/MultilevelTools/mpi/RedistributeToolsTests.jl +++ b/test/MultilevelTools/mpi/RedistributeToolsTests.jl @@ -3,7 +3,7 @@ using MPI, PartitionedArrays include("../RedistributeToolsTests.jl") with_mpi() do distribute - RedistributeToolsTests.main(distribute,4,2,[4,2]) # 2D + RedistributeToolsTests.main(distribute,4,2,[(2,2),(2,1)]) # 2D #RedistributeToolsTests.main(distribute,4,3,[4,2]) # 3D end diff --git a/test/MultilevelTools/mpi/RefinementToolsTests.jl b/test/MultilevelTools/mpi/RefinementToolsTests.jl index 432b45c3..5303294f 100644 --- a/test/MultilevelTools/mpi/RefinementToolsTests.jl +++ b/test/MultilevelTools/mpi/RefinementToolsTests.jl @@ -3,7 +3,7 @@ using MPI, PartitionedArrays include("../RefinementToolsTests.jl") with_mpi() do distribute - RefinementToolsTests.main(distribute,4,2,[4,2,2]) # 2D + RefinementToolsTests.main(distribute,4,2,[(2,2),(2,1),(2,1)]) # 2D #RefinementToolsTests.main(distribute,4,3,[4,2,2]) # 3D end diff --git a/test/MultilevelTools/mpi/runtests.jl b/test/MultilevelTools/mpi/runtests.jl index b43e871a..6002b89b 100644 --- a/test/MultilevelTools/mpi/runtests.jl +++ b/test/MultilevelTools/mpi/runtests.jl @@ -2,15 +2,18 @@ using Test using MPI using GridapSolvers -function run_tests(testdir) +function run_tests(testdir,procs=4) istest(f) = endswith(f, ".jl") && !(f=="runtests.jl") testfiles = sort(filter(istest, readdir(testdir))) @time @testset "$f" for f in testfiles MPI.mpiexec() do cmd - np = 4 - cmd = `$cmd -n $(np) --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))` - @show cmd - run(cmd) + if MPI.MPI_LIBRARY == "OpenMPI" || (isdefined(MPI, :OpenMPI) && MPI.MPI_LIBRARY == MPI.OpenMPI) + run(`$cmd -n $procs --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + else + run(`$cmd -n $procs $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + end + # This line will be reached if and only if the command launched by `run` runs without errors. + # Then, if we arrive here, the test has succeeded. @test true end end diff --git a/test/MultilevelTools/seq/ModelHierarchiesTests.jl b/test/MultilevelTools/seq/ModelHierarchiesTests.jl new file mode 100644 index 00000000..09ed3c5a --- /dev/null +++ b/test/MultilevelTools/seq/ModelHierarchiesTests.jl @@ -0,0 +1,9 @@ +module ModelHierarchiesTestsSeq +using PartitionedArrays +include("../ModelHierarchiesTests.jl") + +with_debug() do distribute + ModelHierarchiesTests.main(distribute,4,[(2,2),(2,2),(2,1)]) +end + +end \ No newline at end of file diff --git a/test/MultilevelTools/seq/runtests.jl b/test/MultilevelTools/seq/runtests.jl index 644ec41b..14f4b016 100644 --- a/test/MultilevelTools/seq/runtests.jl +++ b/test/MultilevelTools/seq/runtests.jl @@ -1 +1,3 @@ -using Test \ No newline at end of file +using Test + +include("ModelHierarchiesTests.jl") diff --git a/test/NonlinearSolvers/mpi/runtests.jl b/test/NonlinearSolvers/mpi/runtests.jl index b43e871a..6002b89b 100644 --- a/test/NonlinearSolvers/mpi/runtests.jl +++ b/test/NonlinearSolvers/mpi/runtests.jl @@ -2,15 +2,18 @@ using Test using MPI using GridapSolvers -function run_tests(testdir) +function run_tests(testdir,procs=4) istest(f) = endswith(f, ".jl") && !(f=="runtests.jl") testfiles = sort(filter(istest, readdir(testdir))) @time @testset "$f" for f in testfiles MPI.mpiexec() do cmd - np = 4 - cmd = `$cmd -n $(np) --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))` - @show cmd - run(cmd) + if MPI.MPI_LIBRARY == "OpenMPI" || (isdefined(MPI, :OpenMPI) && MPI.MPI_LIBRARY == MPI.OpenMPI) + run(`$cmd -n $procs --oversubscribe $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + else + run(`$cmd -n $procs $(Base.julia_cmd()) --project=. $(joinpath(testdir, f))`) + end + # This line will be reached if and only if the command launched by `run` runs without errors. + # Then, if we arrive here, the test has succeeded. @test true end end diff --git a/test/_dev/GMG/GMGPatchBasedTesting.jl b/test/_dev/GMG/GMGPatchBasedTesting.jl index bf94e4b8..0989905e 100644 --- a/test/_dev/GMG/GMGPatchBasedTesting.jl +++ b/test/_dev/GMG/GMGPatchBasedTesting.jl @@ -90,7 +90,7 @@ function get_patch_smoothers(tests,patch_spaces,patch_decompositions,biform,qdeg Ω = Triangulation(PD) dΩ = Measure(Ω,qdegree) a_j(j,v_j) = biform(j,v_j,dΩ) - local_solver = LUSolver() # IS_ConjugateGradientSolver(;reltol=1.e-6) + local_solver = LUSolver() patch_smoother = PatchBasedLinearSolver(a_j,Ph,Vh,local_solver) smoothers[lev] = RichardsonSmoother(patch_smoother,1000,1.0) end diff --git a/test/runtests.jl b/test/runtests.jl index 847972e7..2f7ec348 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,17 +1,8 @@ using GridapSolvers using Test -@testset "Sequential tests" begin - include("MultilevelTools/seq/runtests.jl") - include("LinearSolvers/seq/runtests.jl") - include("NonlinearSolvers/seq/runtests.jl") - include("BlockSolvers/seq/runtests.jl") - include("Applications/seq/runtests.jl") -end - -@testset "MPI tests" begin - include("MultilevelTools/mpi/runtests.jl") - include("LinearSolvers/mpi/runtests.jl") - include("BlockSolvers/mpi/runtests.jl") - include("Applications/mpi/runtests.jl") -end +@testset "MultilevelTools" begin include("MultilevelTools/seq/runtests.jl") end +@testset "LinearSolvers" begin include("LinearSolvers/seq/runtests.jl") end +@testset "NonlinearSolvers" begin include("NonlinearSolvers/seq/runtests.jl") end +@testset "BlockSolvers" begin include("BlockSolvers/seq/runtests.jl") end +@testset "Applications" begin include("Applications/seq/runtests.jl") end diff --git a/test/runtests_mpi.jl b/test/runtests_mpi.jl new file mode 100644 index 00000000..31cd6460 --- /dev/null +++ b/test/runtests_mpi.jl @@ -0,0 +1,7 @@ +using GridapSolvers +using Test + +include("MultilevelTools/mpi/runtests.jl") +include("LinearSolvers/mpi/runtests.jl") +include("BlockSolvers/mpi/runtests.jl") +include("Applications/mpi/runtests.jl")