Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eeaf33d17c | ||
|
|
70cc72c462 | ||
|
|
c6ea42a681 | ||
|
|
61bb7755a3 | ||
|
|
a7fa61464c | ||
|
|
93329a7ba0 | ||
|
|
fb93e21a0c | ||
|
|
19d60f95ec | ||
|
|
861d318162 | ||
|
|
d29e914146 | ||
|
|
b5e99d8d83 | ||
|
|
91f58c293d | ||
|
|
6451e64637 | ||
|
|
b7ac1963bb | ||
|
|
baefb786fb | ||
|
|
99523b3a97 | ||
|
|
59d6820ff3 | ||
|
|
9ec379f176 | ||
|
|
72cc503eb9 | ||
|
|
5aff935c98 | ||
|
|
d56298ba17 | ||
|
|
b9d67dec34 | ||
|
|
5b664e393d | ||
|
|
c582282084 | ||
|
|
944ece4090 | ||
|
|
f225d0c3ef | ||
|
|
a9c98c2e3a | ||
|
|
ac8c2948e7 | ||
|
|
f75b4c10d2 | ||
|
|
ecf6b0b44f | ||
|
|
5a52c676e1 | ||
|
|
5be9693235 | ||
|
|
5421abc4c4 | ||
|
|
f2d3fb45dc | ||
|
|
239c83a742 | ||
|
|
c315028a09 | ||
|
|
99b66f4f45 | ||
|
|
e7be50eb91 | ||
|
|
2dd3915ad5 | ||
|
|
29b572819a | ||
|
|
0a0acfda66 | ||
|
|
7dbce44472 | ||
|
|
c67bd21219 | ||
|
|
38f5b93520 | ||
|
|
a078dfd59e | ||
|
|
66818f5525 | ||
|
|
b810a5e540 | ||
|
|
e63e421343 | ||
|
|
95acb1f85f | ||
|
|
1c7261db07 | ||
|
|
e1b678664a | ||
|
|
8f06539b6e | ||
|
|
30a8eb5ccf | ||
|
|
078e59a33b | ||
|
|
da4e1b5137 | ||
|
|
b5a1660c2d | ||
|
|
5ffc2ef502 | ||
|
|
59cef5f9e3 | ||
|
|
05be944a86 | ||
|
|
c103cfa84a | ||
|
|
29caf08098 | ||
|
|
e217864f16 | ||
|
|
de8aacddce | ||
|
|
f1174bfbf5 | ||
|
|
65acd08d38 | ||
|
|
0acc85d962 | ||
|
|
4e35641c28 | ||
|
|
8594867ab6 | ||
|
|
6e0fbbd3bf | ||
|
|
a2b34ab650 | ||
|
|
f478f687ff | ||
|
|
0bf998510f | ||
|
|
d916299a49 | ||
|
|
10840ac6b4 | ||
|
|
b437016f6e | ||
|
|
5170bbe010 | ||
|
|
f8fcdf6a97 | ||
|
|
d10c908b38 | ||
|
|
89f85cee21 | ||
|
|
00da00b93a | ||
|
|
4eca673111 | ||
|
|
4507a02249 | ||
|
|
61c7187a86 | ||
|
|
4fbefc6987 | ||
|
|
8dd75d2548 | ||
|
|
b0c2ec505f | ||
|
|
a3e229ef82 | ||
|
|
00b6dcdd37 | ||
|
|
19f444489f | ||
|
|
10c637837c | ||
|
|
665ba30f65 | ||
|
|
d6de2c1a1d | ||
|
|
c4b4cfdc2f | ||
|
|
3435475ae0 | ||
|
|
e43b58fa02 | ||
|
|
27c8412439 | ||
|
|
5aaef22cc0 | ||
|
|
f9df36a6de | ||
|
|
b3a75a9295 | ||
|
|
b8c90d24f1 | ||
|
|
a8eba594d2 | ||
|
|
52291cadbf | ||
|
|
1865b430b0 | ||
|
|
d8734b4b18 | ||
|
|
eae1fa217b | ||
|
|
7dc8b0d9fe | ||
|
|
3f236b406f | ||
|
|
47dcdd8159 | ||
|
|
d93d36d6d3 | ||
|
|
6bcad07a5c | ||
|
|
d56493c2c5 | ||
|
|
ec67fe536e | ||
|
|
eff793d6fa | ||
|
|
e5e7280a17 | ||
|
|
773dc57712 | ||
|
|
98a043b195 | ||
|
|
c2a3c83099 | ||
|
|
3ea90aff27 | ||
|
|
23a362d6ad |
@@ -0,0 +1,42 @@
|
||||
# MFEM Pull Request Review Agent Guide
|
||||
|
||||
## Purpose and scope
|
||||
Review MFEM PRs for correctness, maintainability, performance, portability, test coverage, and MFEM consistency. Use the diff and PR context; reference source files, tests, and CI results when available. Follow `CONTRIBUTING.md`, especially Developer Guidelines, PR rules, checklist, and testing.
|
||||
|
||||
## Critical review pillars
|
||||
- Correctness and numerical behavior
|
||||
- API and user-facing impact
|
||||
- Performance implications
|
||||
- Maintainability and portability
|
||||
|
||||
## Review workflow
|
||||
1. Read the PR description, linked issues, and intended behavior.
|
||||
2. Inspect the diff before commenting.
|
||||
3. Identify affected MFEM components, examples, tests, build or docs changes, and downstream APIs.
|
||||
4. Analyze the code against the critical review pillars.
|
||||
5. Compare the change against nearby code and MFEM patterns; flag unmotivated deviations.
|
||||
6. Check whether tests and documentation were updated appropriately.
|
||||
7. Review CI results and suggest actions.
|
||||
8. Produce a structured review with prioritized findings.
|
||||
9. Always limit conclusions to available evidence.
|
||||
|
||||
## MFEM-specific review checklist
|
||||
- Component-aware scope: identify the touched subsystem (FEM, solvers, preconditioners, linear algebra, mesh, examples, miniapps, build, or docs) and assess its impact against the review pillars.
|
||||
- Numerical and algorithmic behavior: assess issues in convergence, stability, tolerances, precision, iteration limits, and failure handling. If clear opportunities exist to improve the algorithmic approach, call them out with expected impact.
|
||||
- API and user-facing impact: assess backward compatibility, user-visible behavior and default changes, migration impact, deprecations, and whether documentation clearly explains user-facing API changes.
|
||||
- Data structure and memory semantics: assess ownership, lifetime, aliasing, container behavior, and device-host synchronization.
|
||||
- Parallel and serial behavior: assess whether the change preserves equivalent semantics in serial and parallel modes where applicable; if logic is currently mode-specific, check whether extension to the other mode is straightforward (clear abstractions, no hard-wired assumptions), document constraints, and call out expected behavior differences explicitly.
|
||||
- Backend and portability impact: assess likely cross-backend risks in CPU, CUDA, HIP, OCCA, RAJA, partial assembly, fallback paths, compiler compatibility, and platform assumptions.
|
||||
- Build, dependency, and configuration impact: assess CMake or make changes, optional dependency behavior, and feature-flag interactions.
|
||||
- Tests and docs alignment: check available regression or unit coverage evidence for changed behavior, and ensure docs are updated for new flags, APIs, options, or behavior changes.
|
||||
- MFEM developer-guideline fit: keep code lean, simple, general, logically separated, and portable; suggest C++17 improvements when they clearly improve safety, clarity, or maintainability.
|
||||
- New source files, examples, or miniapps: if a PR adds source/header files, verify they are properly wired into the relevant `makefile` and `CMakeLists.txt`, referenced in docs where applicable (including `doc/CodeDocumentation.dox`), and added to top-level `.gitignore` only when generated artifacts require it.
|
||||
- Changelog: verify `CHANGELOG` is updated if the PR introduces significant new features or user-facing changes.
|
||||
- MFEM conventions: use `real_t`; use `mfem::out`/`mfem::err` instead of `std::cout`/`std::cerr` in library code; flag large/binary files; if AI assistance is apparent but undisclosed, suggest following `CONTRIBUTING.md`.
|
||||
- Edge cases: if the PR touches complex or error-prone areas, suggest additional tests for edge cases, failure modes, and parallel behavior.
|
||||
|
||||
## Commenting guidelines
|
||||
- Keep comments concise, actionable, and grounded in the diff.
|
||||
- Focus on correctness, behavior changes, and user impact over style nits.
|
||||
- Be professional, concise, collaborative, technically precise, and avoid unsupported assumptions.
|
||||
|
||||
@@ -15,6 +15,10 @@ Version 4.9.1 (development)
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added NVIDIA cuDSS library interface. Implementation examples have been
|
||||
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
|
||||
details. Supported versions >= 0.6.0.
|
||||
|
||||
- Extend FindPointsGSLIB to support surface meshes.
|
||||
|
||||
- Replaced legacy simplex quadrature rules with symmetric positive-weight
|
||||
@@ -39,11 +43,24 @@ Meshing improvements
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
- Added the option to include material interfaces (faces separating elements
|
||||
with different element attributes) as additional boundary elements, for
|
||||
parallel visualization, e.g. with GLVis. This is supported by both the Print
|
||||
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Fixed signed DOF handling in parallel grid-function reading (read constructor)
|
||||
and saving via ParGridFunction::SaveAsOne(). Simplified the process of
|
||||
applying the DOF signs by using the new method ApplyDofSigns() in class
|
||||
ParFiniteElementSpace -- the method will return immediately if no sign flips
|
||||
are needed.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
|
||||
+10
-1
@@ -433,6 +433,15 @@ if (MFEM_USE_STRUMPACK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# cuDSS can only be enabled in CUDA
|
||||
if (MFEM_USE_CUDSS)
|
||||
if (MFEM_USE_CUDA)
|
||||
find_package(CUDSS REQUIRED)
|
||||
else()
|
||||
message(FATAL_ERROR " *** cuDSS requires that CUDA be enabled.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GnuTLS
|
||||
if (MFEM_USE_GNUTLS)
|
||||
find_package(_GnuTLS REQUIRED)
|
||||
@@ -631,7 +640,7 @@ find_package(Threads REQUIRED)
|
||||
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
|
||||
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CUDSS CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
ALGOIM ENZYME CUDA::cudart)
|
||||
|
||||
|
||||
+65
-65
@@ -3,12 +3,13 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml?query=branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -84,7 +85,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
follow the [MFEM PR Rules](#mfem-pr-rules).
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning two
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning two
|
||||
reviewers to evaluate the changes.
|
||||
- The reviewers have 3 weeks to evaluate the PR and work with the author to
|
||||
fix issues and implement improvements.
|
||||
@@ -125,7 +126,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── petsc
|
||||
│ ├── pumi
|
||||
│ ├── sundials
|
||||
| └── superlu
|
||||
│ └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── dfem
|
||||
@@ -137,10 +138,6 @@ The MFEM source code has the following structure:
|
||||
│ ├── moonolith
|
||||
│ ├── qinterp
|
||||
│ └── tmop
|
||||
│ | ├── assemble
|
||||
│ | ├── metrics
|
||||
│ | ├── mult
|
||||
│ | └── tools
|
||||
├── general
|
||||
├── linalg
|
||||
│ ├── batched
|
||||
@@ -153,11 +150,10 @@ The MFEM source code has the following structure:
|
||||
│ ├── common
|
||||
│ ├── contact
|
||||
│ ├── dfem
|
||||
│ ├── diag-smoothers
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── fluids
|
||||
│ │ ├── navier
|
||||
│ │ └── schrodinger-flow
|
||||
│ ├── gslib
|
||||
│ ├── hdiv-linear-solver
|
||||
│ ├── hooke
|
||||
@@ -167,6 +163,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── nurbs
|
||||
│ ├── parelag
|
||||
│ ├── performance
|
||||
│ ├── plasma
|
||||
│ ├── shifted
|
||||
│ ├── solvers
|
||||
│ ├── spde
|
||||
@@ -197,15 +194,15 @@ respectively.
|
||||
|
||||
- The main finite element classes are:
|
||||
+ [`FiniteElement`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElementCollection.html)
|
||||
+ [`FiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1FiniteElementSpace.html)
|
||||
+ [`GridFunction`](https://docs.mfem.org/html/classmfem_1_1GridFunction.html)
|
||||
+ [`BilinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1BilinearFormIntegrator.html) and [`LinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
|
||||
- The main linear algebra classes and sources are
|
||||
+ [`Operator`](https://docs.mfem.org/html/classmfem_1_1Operator.html) and [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1Vector.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`DenseMatrix`](https://docs.mfem.org/html/classmfem_1_1DenseMatrix.html) and [`SparseMatrix`](https://docs.mfem.org/html/classmfem_1_1SparseMatrix.html)
|
||||
+ Sparse [smoothers](https://docs.mfem.org/html/sparsesmoothers_8hpp.html) and linear [solvers](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
|
||||
@@ -217,8 +214,8 @@ shared geometric entities between different tasks. The parallel source files
|
||||
have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
|
||||
- The main parallel classes are
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParNCMesh.html)
|
||||
+ [`ParFiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1ParFiniteElementSpace.html)
|
||||
+ [`ParGridFunction`](https://docs.mfem.org/html/classmfem_1_1ParGridFunction.html)
|
||||
+ [`ParBilinearForm`](https://docs.mfem.org/html/classmfem_1_1ParBilinearForm.html) and [`ParLinearForm`](https://docs.mfem.org/html/classmfem_1_1ParLinearForm.html)
|
||||
@@ -228,14 +225,14 @@ have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
#### GPU and general device support
|
||||
|
||||
GPU and multi-core CPU support is based on device kernels supporting different
|
||||
backends (CUDA, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
backends (CUDA, HIP, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
device/host memory manager.
|
||||
|
||||
- The main device-relevant classes and sources are:
|
||||
+ [`Device`](https://docs.mfem.org/html/device_8hpp.html)
|
||||
+ [`MemoryManager`](https://docs.mfem.org/html/mem_manager_8hpp.html)
|
||||
+ the [`mfem::forall`](https://docs.mfem.org/html/forall_8hpp.html) function
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html), [`hip.hpp`](https://docs.mfem.org/html/hip_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
|
||||
#### Utilities, building and documentation
|
||||
- The `general/` directory contains C++ classes that serve as utilities for
|
||||
@@ -249,8 +246,8 @@ device/host memory manager.
|
||||
- `examples` and `miniapps` respectively gather simple and more fully-featured
|
||||
demonstrations of the usage on MFEM. They both rely on `data/` for the
|
||||
collection of meshes.
|
||||
- The `tests/` directory contains a unit test suite and will later contain more
|
||||
tests that run example codes.
|
||||
- The `tests/` directory contains a unit test suite, additional tests, and
|
||||
benchmarks.
|
||||
|
||||
See also the [code overview](https://mfem.org/code-overview/) section on the MFEM
|
||||
website.
|
||||
@@ -284,8 +281,8 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
the top of https://github.com/mfem.
|
||||
- Consider making your membership public by going to https://github.com/orgs/mfem/people
|
||||
and clicking on the organization visibility drop box next to your name.
|
||||
- Project discussions and announcements will be posted at
|
||||
https://github.com/orgs/mfem/teams/everyone.
|
||||
- Project discussions and announcements will be posted at https://github.com/orgs/mfem/discussions,
|
||||
tagging the `@mfem/everyone` team when appropriate.
|
||||
|
||||
#### Structure
|
||||
- The MFEM source code is in the [mfem](https://github.com/mfem/mfem)
|
||||
@@ -345,11 +342,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Well-designed simple code is frequently more general and powerful.
|
||||
- Lean code base is easier to understand by new collaborators.
|
||||
- New features should be added only if they are necessary or generally useful.
|
||||
- Introduction of language constructions not currently used in MFEM should be
|
||||
- Introduction of language constructs not currently used in MFEM should be
|
||||
justified and generally avoided (to maintain portability to various systems
|
||||
and compilers, including early access hardware).
|
||||
- We prefer basic C++ and the C++03 standard, to keep the code readable by
|
||||
a large audience and to make sure it compiles anywhere.
|
||||
- We prefer basic C++. Use C++17 features judiciously, prioritizing readability,
|
||||
consistency with existing MFEM code, and portability to different systems,
|
||||
compilers and device backends.
|
||||
|
||||
- *Keep the code general and reasonably efficient*
|
||||
- The main goal is fast prototyping for research and application development.
|
||||
@@ -392,7 +390,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- When your branch is ready for other developers to review / comment on
|
||||
the code, create a pull request towards `mfem:master`.
|
||||
|
||||
- Pull request typically have titles like:
|
||||
- Pull requests typically have titles like:
|
||||
|
||||
`Description [new-feature-dev]`
|
||||
|
||||
@@ -413,12 +411,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Add a description, appropriate labels and assign yourself to the PR. The MFEM
|
||||
team will add reviewers as appropriate.
|
||||
|
||||
- List outstanding TODO items in the description, see PR #222 for an example.
|
||||
- List outstanding TODO items in the description.
|
||||
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
or request the `ready-for-review` label.
|
||||
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning
|
||||
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
|
||||
the PR and work with the author to implement improvements and fix issues.
|
||||
|
||||
@@ -444,7 +442,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
checks in GitHub Actions enforce MFEM-specific rules which are explained in
|
||||
the error messages and the `tests/scripts` directory.
|
||||
|
||||
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
|
||||
- Also note that the tests `branch-history` and `repo-check` found in GitHub
|
||||
Actions can be triggered automatically before each push using git hooks. See
|
||||
the [git hooks README](config/githooks/README.md) for a detailed explanation.
|
||||
|
||||
@@ -501,15 +499,15 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a
|
||||
|
||||
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
|
||||
|
||||
3. To seek help from the editors in case of difficulties.
|
||||
4. To seek help from the editors in case of difficulties.
|
||||
|
||||
4. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
5. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
|
||||
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
6. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
7. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
|
||||
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
8. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
|
||||
#### Responsibilities of Authors
|
||||
|
||||
@@ -535,30 +533,30 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Code builds.
|
||||
- [ ] Code passes `make style`.
|
||||
- [ ] Update `CHANGELOG`:
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Update `INSTALL`:
|
||||
- [ ] Had a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Has a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update `.gitignore`:
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] New examples:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] Add the example code to the appropriate `SEQ_EXAMPLES` and `PAR_EXAMPLES` variables.
|
||||
- [ ] Add any files generated by it to the `clean` target.
|
||||
- [ ] Add the example binary and any files generated by it to the top-level `.gitignore` file.
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Add the example code to the `ALL_EXE_SRCS` variable.
|
||||
- [ ] Make sure `THIS_TEST_OPTIONS` is set correctly for the new example.
|
||||
- [ ] List the new example in `doc/CodeDocumentation.dox`.
|
||||
- [ ] If new examples directory (e.g.`examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new examples directory (e.g. `examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add example-specific documentation, see e.g. the `src/examples.md`.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
@@ -575,13 +573,13 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Add/update the `CMakeLists.txt` file in the new miniapp directory.
|
||||
- [ ] Consider adding a new test for the new miniapp.
|
||||
- [ ] List the new miniapp in `doc/CodeDocumentation.dox`
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New capability:
|
||||
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
|
||||
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
|
||||
@@ -683,7 +681,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- [ ] Update URL shortlinks:
|
||||
- [ ] Create a shortlink at [http://bit.ly/](http://bit.ly/) for the release tarball, e.g. https://mfem.github.io/releases/mfem-3.1.tgz.
|
||||
- [ ] (LLNL only) Add and commit the new shortlink in the `links` and `links-mfem` files of the internal `mfem/downloads` repo.
|
||||
- [ ] Add the new shortlinks to the MFEM packages in `spack`, `homebrew/science`, `VisIt`, etc.
|
||||
- [ ] Add the new shortlinks to the MFEM package in `spack`.
|
||||
- [ ] Update website in `mfem/web` repo:
|
||||
- Update version and shortlinks in `src/index.md` and `src/download.md`.
|
||||
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
|
||||
@@ -735,22 +733,24 @@ commit or push, see the [README](config/githooks/README.md) in the `config/githo
|
||||
directory.
|
||||
|
||||
|
||||
### Linux and Mac smoke tests
|
||||
### GitHub Actions smoke tests
|
||||
|
||||
We use GitHub Actions to drive the default tests on the `master` and `next`
|
||||
branches. See the `.github/workflows` files and the logs at
|
||||
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
|
||||
|
||||
Testing using GitHub Actions should be kept lightweight, as there is a time
|
||||
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
|
||||
GitHub Actions testing should be kept lightweight, as there is a time
|
||||
constraint on jobs. The current workflows cover Linux, macOS, and Windows
|
||||
configurations.
|
||||
|
||||
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
|
||||
- Tests on the `next` branch are currently scheduled to run each night.
|
||||
|
||||
### Additional Windows smoke test
|
||||
|
||||
### Windows smoke test
|
||||
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor` file and the
|
||||
build logs at
|
||||
We also use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor.yml` file
|
||||
and the build logs at
|
||||
[https://ci.appveyor.com/project/mfem/mfem](https://ci.appveyor.com/project/mfem/mfem).
|
||||
|
||||
CMake is used to generate the MSVC Project files and drive the build. A release
|
||||
|
||||
@@ -38,14 +38,13 @@ the option MFEM_USE_METIS.
|
||||
MFEM also includes support for devices such as GPUs, and programming models such
|
||||
as CUDA, HIP, OCCA, OpenMP and RAJA.
|
||||
|
||||
- Starting with version 4.0, MFEM requires a C++11 compiler. We recommend using
|
||||
a newer compiler, e.g. GCC version 4.9 or higher.
|
||||
- Starting with version 4.9, MFEM requires a C++17 compiler.
|
||||
|
||||
- CUDA support requires an NVIDIA GPU and an installation of the CUDA Toolkit
|
||||
https://developer.nvidia.com/cuda-toolkit
|
||||
|
||||
- HIP support requires an AMD GPU and an installation of the ROCm software stack
|
||||
https://rocmdocs.amd.com
|
||||
https://rocm.docs.amd.com
|
||||
|
||||
- OCCA support requires the OCCA library
|
||||
https://libocca.org
|
||||
@@ -83,9 +82,9 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
make parallel -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
CUDA build:
|
||||
make cuda -j 4
|
||||
@@ -115,14 +114,14 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
|
||||
make -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
Parallel build with fetching of hypre and METIS:
|
||||
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DMFEM_FETCH_TPLS=YES
|
||||
make -j 4
|
||||
|
||||
@@ -134,7 +133,8 @@ CUDA build:
|
||||
|
||||
HIP build:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 \
|
||||
-DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
make -j 4
|
||||
|
||||
Example codes (serial/parallel, depending on the build):
|
||||
@@ -269,6 +269,7 @@ Compilers:
|
||||
CXX - C++ compiler, serial build
|
||||
MPICXX - MPI C++ compiler, parallel build
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
|
||||
HIP_CXX - The HIP compiler, e.g. 'hipcc'
|
||||
|
||||
Compiler options:
|
||||
OPTIM_FLAGS - Options for optimized build
|
||||
@@ -395,6 +396,11 @@ MFEM_USE_STRUMPACK = YES/NO
|
||||
classes. When enabled, this option uses the STRUMPACK_* library options, see
|
||||
below.
|
||||
|
||||
MFEM_USE_CUDSS = YES/NO
|
||||
Enable MFEM functionality based on the cuDSS library. When using cuDSS, CUDA
|
||||
support must be also enabled in MFEM, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
When enabled, this option uses the CUDSS_* library options, see below.
|
||||
|
||||
MFEM_USE_GINKGO = YES/NO
|
||||
Enable MFEM functionality based on the Ginkgo library, which provides
|
||||
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
|
||||
@@ -554,13 +560,13 @@ MFEM_USE_RAJA = YES/NO
|
||||
MFEM_USE_OCCA = YES/NO
|
||||
Enables support for the OCCA library in MFEM. OCCA is an open-source library
|
||||
which aims to make it easy to program different types of devices (e.g. CPU,
|
||||
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
|
||||
GPU, FPGA) by providing a unified API for interacting with JIT-compiled
|
||||
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
|
||||
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
|
||||
MFEM_USE_GSLIB = YES/NO
|
||||
Enables MFEM functionality based on the GSLIB library, and specifically its
|
||||
FindPoints component, which provides a robust algorithms to evaluate finite
|
||||
FindPoints component, which provides robust algorithms to evaluate finite
|
||||
element functions in a collection of points in physical space. When enabled,
|
||||
the user can use the GSLIB-FindPoints methods as shown in miniapps/gslib.
|
||||
|
||||
@@ -719,9 +725,18 @@ The specific libraries and their options are:
|
||||
Options: STRUMPACK_OPT, STRUMPACK_LIB.
|
||||
Versions: STRUMPACK >= 3.0.0.
|
||||
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
|
||||
C++ compiler that supports the C++-17 standard. For additional requirements
|
||||
and dependencies of specific modules, see the Ginkgo webpage below.
|
||||
- CUDSS (optional), used when MFEM_USE_CUDSS = YES. Note that CUDSS requires
|
||||
CUDA 12.x toolkit and the cuDSS libraries. The supported communication backend
|
||||
is OpenMPI 4.x (default), and OpenMPI 4.x or a later version must be pre-built.
|
||||
The source files in the cuDSS tarball provide guidance for developing custom
|
||||
MPI implementations.
|
||||
URL: https://developer.nvidia.com/cudss
|
||||
https://docs.nvidia.com/cuda/cudss/advanced_features.html#communication-layer-library-in-cudss
|
||||
Options: CUDSS_OPT, CUDSS_LIB.
|
||||
Versions: cuDSS >= 0.6.0.
|
||||
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Ginkgo may have additional
|
||||
requirements and module-specific dependencies; see the webpage below.
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
|
||||
Debug).
|
||||
@@ -793,7 +808,7 @@ The specific libraries and their options are:
|
||||
Options: CONDUIT_OPT, CONDUIT_LIB.
|
||||
Versions: Conduit >= 0.3.1.
|
||||
|
||||
- ADIOS2 (optional) used when MFEM_USE_ADIOS2 = YES.
|
||||
- ADIOS2 (optional), used when MFEM_USE_ADIOS2 = YES.
|
||||
URL: https://adios2.readthedocs.io/
|
||||
Versions: ADIOS >= 2.5.0.
|
||||
|
||||
@@ -869,7 +884,7 @@ The specific libraries and their options are:
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 2022.10.3.
|
||||
|
||||
- Moonolith (optional), use when MFEM_USE_MOONOLITH = YES.
|
||||
- Moonolith (optional), used when MFEM_USE_MOONOLITH = YES.
|
||||
URL: https://bitbucket.org/zulianp/par_moonolith
|
||||
Options: MOONOLITH_DIR
|
||||
Versions: MOONOLITH >= 1.1.0.
|
||||
@@ -957,7 +972,7 @@ CMAKE_BUILD_TYPE which can be set to standard values like "Debug", and "Release"
|
||||
To use a specific generator use the "-G <generator>" option of cmake:
|
||||
|
||||
cmake <mfem-source-dir> -G "Xcode"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 12 2013"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 17 2022"
|
||||
cmake <mfem-source-dir> -G "MinGW Makefiles"
|
||||
|
||||
With CMake it is possible to build MFEM as a shared library using the standard
|
||||
@@ -1202,7 +1217,7 @@ larger problems, there are two options:
|
||||
Specific options for HIP
|
||||
========================
|
||||
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
|
||||
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
ROCm install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
|
||||
Specific options for RAJA+HIP+MPI
|
||||
=================================
|
||||
|
||||
@@ -35,6 +35,7 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
|
||||
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
|
||||
set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
|
||||
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
|
||||
set(MFEM_USE_CUDSS @MFEM_USE_CUDSS@)
|
||||
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
|
||||
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
|
||||
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
|
||||
@@ -109,6 +110,10 @@ if (MFEM_USE_RAJA)
|
||||
find_dependency(RAJA)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CUDSS)
|
||||
find_dependency(cudss)
|
||||
endif (MFEM_USE_CUDSS)
|
||||
|
||||
if (MFEM_USE_UMPIRE)
|
||||
find_dependency(umpire)
|
||||
endif()
|
||||
|
||||
@@ -108,6 +108,15 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
#cmakedefine MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
#cmakedefine MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
#cmakedefine MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
#cmakedefine MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable functionality based on the Ginkgo library.
|
||||
#cmakedefine MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
if (NOT cudss_DIR AND CUDSS_DIR)
|
||||
set(cudss_DIR ${CUDSS_DIR}/lib/cmake/cudss)
|
||||
endif()
|
||||
message(STATUS "Looking for CUDSS ...")
|
||||
message(STATUS " in CUDSS_DIR = ${CUDSS_DIR}")
|
||||
message(STATUS " cudss_DIR = ${cudss_DIR}")
|
||||
find_package(cudss)
|
||||
set(CUDSS_FOUND ${cudss_FOUND})
|
||||
set(CUDSS_LIBRARIES "cudss")
|
||||
if (CUDSS_FOUND)
|
||||
message(STATUS
|
||||
"Found CUDSS target: ${CUDSS_LIBRARIES} (version: ${cudss_VERSION})")
|
||||
else()
|
||||
set(msg STATUS)
|
||||
if (CUDSS_FIND_REQUIRED)
|
||||
set(msg FATAL_ERROR)
|
||||
endif()
|
||||
message(${msg}
|
||||
"CUDSS not found. Please set CUDSS_DIR to the install prefix.")
|
||||
endif()
|
||||
|
||||
if(CUDSS_FOUND AND TARGET cudss)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION)
|
||||
if(NOT CUDSS_LIBRARY_LOCATION)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
if(CUDSS_LIBRARY_LOCATION)
|
||||
get_filename_component(CUDSS_LIBRARY_DIR "${CUDSS_LIBRARY_LOCATION}" DIRECTORY)
|
||||
else()
|
||||
message(WARNING "Could not determine the location of the cuDSS library.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "cuDSS target not available; cannot determine library directory.")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS threading library if OpenMP is enabled.
|
||||
# The threading layer library (libcudss_mtlayer_gomp.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
if (MFEM_USE_OPENMP)
|
||||
find_file(
|
||||
CUDSS_THREADING_LIB
|
||||
NAMES libcudss_mtlayer_gomp.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_THREADING_LIB AND CUDSS_THREADING_LIB)
|
||||
set(MFEM_CUDSS_THREADING_LIB "${CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS threading layer library: ${MFEM_CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS communication library if MFEM use OpenMPI.
|
||||
# The communication layer library (libcudss_commlayer_mpi.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
# The communication layer library is used pre-built communication layers for OpenMPI
|
||||
# by default.
|
||||
if (MFEM_USE_MPI)
|
||||
find_file(
|
||||
CUDSS_COMM_LIB
|
||||
NAMES libcudss_commlayer_openmpi.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_COMM_LIB AND CUDSS_COMM_LIB)
|
||||
set(MFEM_CUDSS_COMM_LIB "${CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS communication layer library: ${MFEM_CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
@@ -157,4 +157,10 @@ constexpr real_t operator""_r(unsigned long long v)
|
||||
#endif
|
||||
#endif // MFEM_USE_MPI not defined
|
||||
|
||||
#ifndef MFEM_USE_CUDA
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
#error Building with cuDSS (MFEM_USE_CUDSS=YES) requires CUDA (MFEM_USE_CUDA=YES)
|
||||
#endif
|
||||
#endif // MFEM_USE_CUDSS not defined
|
||||
|
||||
#endif // MFEM_CONFIG_HPP
|
||||
|
||||
@@ -108,6 +108,15 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
// #define MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
// #define MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
// #define MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
// #define MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable MFEM features based on the Ginkgo library.
|
||||
// #define MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@ MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
|
||||
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
|
||||
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
|
||||
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
|
||||
MFEM_USE_CUDSS = @MFEM_USE_CUDSS@
|
||||
MFEM_CUDSS_COMM_LIB = @MFEM_CUDSS_COMM_LIB@
|
||||
MFEM_CUDSS_THREADING_LIB = @MFEM_CUDSS_THREADING_LIB@
|
||||
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
|
||||
|
||||
@@ -38,6 +38,7 @@ option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
|
||||
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
|
||||
option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
|
||||
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
|
||||
option(MFEM_USE_CUDSS "Enable cuDSS usage" OFF)
|
||||
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
|
||||
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
|
||||
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
|
||||
|
||||
+15
-1
@@ -153,6 +153,7 @@ MFEM_USE_SUPERLU = NO
|
||||
MFEM_USE_SUPERLU5 = NO
|
||||
MFEM_USE_MUMPS = NO
|
||||
MFEM_USE_STRUMPACK = NO
|
||||
MFEM_USE_CUDSS = NO
|
||||
MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_MAGMA = NO
|
||||
@@ -368,6 +369,19 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
|
||||
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
|
||||
$(SCOTCH_LIB) $(SCALAPACK_LIB)
|
||||
|
||||
# CUDSS library configuration
|
||||
CUDSS_DIR = @MFEM_DIR@/../cudss
|
||||
CUDSS_INCLUDE_DIR = $(CUDSS_DIR)/include
|
||||
CUDSS_LIBRARY_DIR = $(CUDSS_DIR)/lib
|
||||
CUDSS_OPT = -I$(CUDSS_INCLUDE_DIR)
|
||||
CUDSS_LIB = \
|
||||
$(XLINKER)-rpath,$(CUDSS_LIBRARY_DIR) -L$(CUDSS_LIBRARY_DIR) -lcudss
|
||||
# The cuDSS communication and threading libraries.
|
||||
MFEM_CUDSS_COMM_LIB = $(abspath $(wildcard $(or $(CUDSS_COMM_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR), $(CUDSS_LIBRARY_DIR)/libcudss_commlayer_openmpi.so))))
|
||||
MFEM_CUDSS_THREADING_LIB = $(abspath $(wildcard $(or $(CUDSS_THREADING_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR),$(CUDSS_LIBRARY_DIR)/libcudss_mtlayer_gomp.so))))
|
||||
|
||||
# Ginkgo library configuration
|
||||
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
|
||||
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
|
||||
@@ -621,7 +635,7 @@ PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
|
||||
AXOM_DIR = @MFEM_DIR@/../axom
|
||||
TRIBOL_DIR = @MFEM_DIR@/../tribol
|
||||
TRIBOL_OPT = -I$(TRIBOL_DIR)/include -I$(AXOM_DIR)/include
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -ltribol_shared -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
+23
-11
@@ -224,17 +224,29 @@ int main(int argc, char *argv[])
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
// Use cuDSS to solve the system.
|
||||
CuDSSSolver cudss_solver;
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -273,7 +285,7 @@ int main(int argc, char *argv[])
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
|
||||
+47
-22
@@ -83,6 +83,9 @@ int main(int argc, char *argv[])
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool algebraic_ceed = false;
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
bool cudss_solver = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -102,6 +105,10 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&algebraic_ceed, "-a", "--algebraic",
|
||||
"-no-a", "--no-algebraic",
|
||||
"Use algebraic Ceed solver");
|
||||
#endif
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
args.AddOption(&cudss_solver, "-cudss", "--cudss-solver", "-no-cudss",
|
||||
"--no-cudss-solver", "Use the cuDSS Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -248,33 +255,51 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (!pa && (Device::Allows(Backend::CUDA_MASK) && cudss_solver))
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
// Solve using a direct solver with cuDSS
|
||||
CuDSSSolver cudss_solver(MPI_COMM_WORLD);
|
||||
cudss_solver.SetMatrixSymType(
|
||||
CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE);
|
||||
cudss_solver.SetMatrixViewType(CuDSSSolver::UPPER);
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec)
|
||||
{
|
||||
cg.SetPreconditioner(*prec);
|
||||
}
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
|
||||
+13
-26
@@ -830,15 +830,9 @@ ParComplexGridFunction::ParComplexGridFunction(ParMesh *m, std::istream &input)
|
||||
int vsize = pfes->GetVSize();
|
||||
Vector::Load(input, 2*vsize);
|
||||
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
real_t *h_data = HostReadWrite();
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
|
||||
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
|
||||
@@ -1051,15 +1045,14 @@ void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
os << '\n';
|
||||
|
||||
int vsize = pfes->GetVSize();
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t *h_data = const_cast<real_t*>(HostRead());
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
if (pfes->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
@@ -1070,14 +1063,8 @@ void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
Vector::Print(os, pfes->GetVDim());
|
||||
}
|
||||
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
os.flush();
|
||||
}
|
||||
|
||||
@@ -114,6 +114,10 @@ void ConduitDataCollection::Save()
|
||||
n_mesh["fields"][name]);
|
||||
}
|
||||
|
||||
// TODO: in parallel, we need to call ParFiniteElementSpace::ApplyDofSigns
|
||||
// for all ParGridFunction objects before and after saving, see
|
||||
// ParGridFunction::Save.
|
||||
|
||||
// save mesh data
|
||||
SaveMeshAndFields(myid,
|
||||
n_mesh,
|
||||
|
||||
+26
-8
@@ -3363,18 +3363,36 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
int gf_order_h1 = std::max(gf_order, 1); // H1 should be at least order 1
|
||||
H1_FECollection fec(gf_order_h1, dim);
|
||||
const int ncomp = field_in.FESpace()->GetVDim();
|
||||
FiniteElementSpace fes(mesh, &fec, ncomp,
|
||||
field_in.FESpace()->GetOrdering());
|
||||
GridFunction field_in_h1(&fes);
|
||||
field_in_h1.UseDevice(false);
|
||||
|
||||
std::unique_ptr<FiniteElementSpace> fes;
|
||||
std::unique_ptr<GridFunction> field_in_h1;
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (auto *pmesh = dynamic_cast<ParMesh*>(mesh))
|
||||
{
|
||||
fes = std::make_unique<ParFiniteElementSpace>(
|
||||
pmesh, &fec, ncomp, field_in.FESpace()->GetOrdering());
|
||||
|
||||
field_in_h1 = std::make_unique<ParGridFunction>(
|
||||
static_cast<ParFiniteElementSpace*>(fes.get()));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
fes = std::make_unique<FiniteElementSpace>(
|
||||
mesh, &fec, ncomp, field_in.FESpace()->GetOrdering());
|
||||
|
||||
field_in_h1 = std::make_unique<GridFunction>(fes.get());
|
||||
}
|
||||
|
||||
field_in_h1->UseDevice(false);
|
||||
|
||||
if (avgtype == AvgType::ARITHMETIC)
|
||||
{
|
||||
field_in_h1.ProjectDiscCoefficient(field_in_dg, GridFunction::ARITHMETIC);
|
||||
field_in_h1->ProjectDiscCoefficient(field_in_dg, GridFunction::ARITHMETIC);
|
||||
}
|
||||
else if (avgtype == AvgType::HARMONIC)
|
||||
{
|
||||
field_in_h1.ProjectDiscCoefficient(field_in_dg, GridFunction::HARMONIC);
|
||||
field_in_h1->ProjectDiscCoefficient(field_in_dg, GridFunction::HARMONIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3383,11 +3401,11 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
|
||||
if (gf_order_h1 == mesh_order) // basis is GaussLobatto by default
|
||||
{
|
||||
InterpolateH1(field_in_h1, field_out_l2, field_out_ordering);
|
||||
InterpolateH1(*field_in_h1, field_out_l2, field_out_ordering);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in_h1, field_out_l2, field_out_ordering);
|
||||
InterpolateGeneral(*field_in_h1, field_out_l2, field_out_ordering);
|
||||
}
|
||||
|
||||
// Copy interpolated values for the points on element border
|
||||
|
||||
+4
-4
@@ -284,12 +284,12 @@ GeometricMultigrid::GeometricMultigrid(
|
||||
ownedProlongations.SetSize(nlevels - 1);
|
||||
ownedProlongations = have_ess_bdr;
|
||||
|
||||
if (have_ess_bdr)
|
||||
essentialTrueDofs.SetSize(nlevels);
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
{
|
||||
essentialTrueDofs.SetSize(nlevels);
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
essentialTrueDofs[level] = new Array<int>;
|
||||
if (have_ess_bdr)
|
||||
{
|
||||
essentialTrueDofs[level] = new Array<int>;
|
||||
fespaces.GetFESpaceAtLevel(level).GetEssentialTrueDofs(
|
||||
ess_bdr, *essentialTrueDofs[level]);
|
||||
}
|
||||
|
||||
+1
-2
@@ -187,8 +187,7 @@ public:
|
||||
/// mesh boundary element attributes that define the essential DOFs.
|
||||
///
|
||||
/// If @a ess_bdr is empty, or all its entries are 0, then no essential
|
||||
/// boundary conditions are imposed and the protected array essentialTrueDofs
|
||||
/// remains empty.
|
||||
/// boundary conditions are imposed.
|
||||
GeometricMultigrid(const FiniteElementSpaceHierarchy& fespaces_,
|
||||
const Array<int> &ess_bdr);
|
||||
|
||||
|
||||
+31
-3
@@ -349,6 +349,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
}
|
||||
}
|
||||
|
||||
bool have_sign_flips = false;
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
g_ldof_sign->SetSize(GetNDofs());
|
||||
@@ -428,6 +429,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
have_sign_flips = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -466,6 +468,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
have_sign_flips = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -504,6 +507,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
have_sign_flips = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -527,12 +531,18 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
group_ldof.GetI()[gr+1] = group_ldof_counter;
|
||||
}
|
||||
|
||||
if (g_ldof_sign && have_sign_flips == false)
|
||||
{
|
||||
g_ldof_sign->DeleteAll();
|
||||
}
|
||||
|
||||
gc.Finalize();
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(Conforming(), "wrong code path");
|
||||
if (!HaveDofSigns()) { return; }
|
||||
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
@@ -559,6 +569,24 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
|
||||
ApplyLDofSigns(all_dofs);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::ApplyDofSigns(real_t *h_data) const
|
||||
{
|
||||
if (!HaveDofSigns()) { return; }
|
||||
|
||||
const bool byvdim = (ordering == Ordering::byVDIM);
|
||||
for (int i = 0; i < ndofs; i++)
|
||||
{
|
||||
if (ldof_sign[i] < 0)
|
||||
{
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
const int idx = byvdim ? d+vdim*i : i+ndofs*d;
|
||||
h_data[idx] = -h_data[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
@@ -1193,15 +1221,15 @@ void ParFiniteElementSpace::GetEssentialTrueDofsVar(const Array<int>
|
||||
MFEM_VERIFY(IsVariableOrder() && R,
|
||||
"GetEssentialTrueDofsVar is only for variable-order spaces");
|
||||
|
||||
true_ess_dofs.SetSize(R->Height(), Device::GetDeviceMemoryType());
|
||||
true_ess_dofs.SetSize(R->Height());
|
||||
true_ess_dofs.HostWrite();
|
||||
true_ess_dofs = 0;
|
||||
|
||||
const int ntdofs = tdof2ldof.Size();
|
||||
MFEM_VERIFY(vdim * ntdofs == R->NumRows() &&
|
||||
vdim * ntdofs == true_ess_dofs.Size(), "");
|
||||
MFEM_VERIFY(ldof_ltdof.Size() == ndofs && ess_dofs.Size() == vdim * ndofs, "");
|
||||
|
||||
true_ess_dofs = 0;
|
||||
|
||||
const bool bynodes = (ordering == Ordering::byNODES);
|
||||
const int vdim_factor = bynodes ? 1 : vdim;
|
||||
const int num_true_dofs = R->NumRows() / vdim;
|
||||
|
||||
+14
-2
@@ -340,8 +340,20 @@ public:
|
||||
|
||||
inline ParMesh *GetParMesh() const { return pmesh; }
|
||||
|
||||
int GetDofSign(int i)
|
||||
{ return NURBSext || Nonconforming() ? 1 : ldof_sign[VDofToDof(i)]; }
|
||||
/** @brief Return true if the parallel FE space has DOFs with signs opposite
|
||||
of the DOFs in the respective serial FE space. */
|
||||
bool HaveDofSigns() const { return ldof_sign.Size() != 0; }
|
||||
|
||||
/** @brief Apply the DOF signs to the given host data @a h_data which must be
|
||||
of size GetVSize() if HaveDofSigns() is true. If HaveDofSigns() is false,
|
||||
this method is no-op and returns immediately. */
|
||||
void ApplyDofSigns(real_t *h_data) const;
|
||||
|
||||
/** @brief Return -1 if the given (vector) DOF @a i has a sign opposite of
|
||||
the DOF in the respecive serial FE space. Otherwise, return 1. */
|
||||
int GetDofSign(int i) const
|
||||
{ return !HaveDofSigns() ? 1 : ldof_sign[VDofToDof(i)]; }
|
||||
|
||||
HYPRE_BigInt *GetDofOffsets() const { return dof_offsets; }
|
||||
HYPRE_BigInt *GetTrueDofOffsets() const { return tdof_offsets; }
|
||||
HYPRE_BigInt GlobalVSize() const
|
||||
|
||||
+18
-9
@@ -80,6 +80,8 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, std::istream &input)
|
||||
fes->GetOrdering());
|
||||
delete fes;
|
||||
fes = pfes;
|
||||
|
||||
pfes->ApplyDofSigns(HostReadWrite());
|
||||
}
|
||||
|
||||
void ParGridFunction::Update()
|
||||
@@ -1082,18 +1084,17 @@ real_t ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
|
||||
void ParGridFunction::Save(std::ostream &os) const
|
||||
{
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
|
||||
}
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t *h_data = const_cast<real_t*>(HostRead());
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
|
||||
GridFunction::Save(os);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
|
||||
}
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
}
|
||||
|
||||
void ParGridFunction::Save(const char *fname, int precision) const
|
||||
@@ -1264,7 +1265,13 @@ void ParGridFunction::SaveAsOne(std::ostream &os) const
|
||||
int *nfdofs = new int[NRanks];
|
||||
int *nrdofs = new int[NRanks];
|
||||
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t * h_data = const_cast<real_t *>(this->HostRead());
|
||||
pfes->ApplyDofSigns(h_data); // temporarily flip the dof signs
|
||||
|
||||
values[0] = h_data;
|
||||
nv[0] = pfes -> GetVSize();
|
||||
@@ -1371,6 +1378,8 @@ void ParGridFunction::SaveAsOne(std::ostream &os) const
|
||||
MPI_Send(h_data, nv[0], MPITypeMap<real_t>::mpi_type, 0, 460, MyComm);
|
||||
}
|
||||
|
||||
pfes->ApplyDofSigns(h_data); // restore the original h_data
|
||||
|
||||
delete [] values;
|
||||
delete [] nv;
|
||||
delete [] nvdofs;
|
||||
|
||||
@@ -148,6 +148,12 @@ if (MFEM_USE_MKL_PARDISO)
|
||||
list(APPEND HDRS pardiso.hpp)
|
||||
endif()
|
||||
|
||||
# cudss solver
|
||||
if (MFEM_USE_CUDSS)
|
||||
list(APPEND SRCS cudss.cpp)
|
||||
list(APPEND HDRS cudss.hpp)
|
||||
endif()
|
||||
|
||||
convert_filenames_to_full_paths(SRCS)
|
||||
convert_filenames_to_full_paths(HDRS)
|
||||
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "cudss.hpp"
|
||||
#include "../general/communication.hpp"
|
||||
#include <string>
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
#define CUDA_REAL_T CUDA_R_32F
|
||||
#else
|
||||
#define CUDA_REAL_T CUDA_R_64F
|
||||
#endif
|
||||
|
||||
// Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x), where x returns/is of
|
||||
// type 'cudssStatus_t'. This macro evaluates 'x' and raises an error if the
|
||||
// result is not CUDSS_STATUS_SUCCESS.
|
||||
#define MFEM_CUDSS_CHECK(x) \
|
||||
do { \
|
||||
cudssStatus_t mfem_err_internal_var_name = (x); \
|
||||
if (mfem_err_internal_var_name != CUDSS_STATUS_SUCCESS) { \
|
||||
::mfem::mfem_cudss_error(mfem_err_internal_var_name, #x, \
|
||||
_MFEM_FUNC_NAME, __FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
// Function used by the macro MFEM_CUDSS_CHECK.
|
||||
void mfem_cudss_error(cudssStatus_t status, const char *expr, const char *func,
|
||||
const char *file, int line)
|
||||
{
|
||||
mfem::err << "\n\nCUDSS error: (" << expr << ") failed with error:\n --> "
|
||||
<< "CUDSS call ended unsuccessfully"
|
||||
<< " [code: " << static_cast<int>(status) << ']'
|
||||
<< "\n ... in function: " << func << "\n ... in file: " << file
|
||||
<< ':' << line << '\n';
|
||||
mfem_error();
|
||||
}
|
||||
|
||||
CuDSSSolver::CuDSSSolver() { InitCuDSS(); }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
CuDSSSolver::CuDSSSolver(MPI_Comm comm_) : mpi_comm(comm_)
|
||||
{
|
||||
InitCuDSS();
|
||||
|
||||
// NOTE: Set the communication layer to NULL so that cuDSS picks it
|
||||
// from the environment variable "CUDSS_COMM_LIB"
|
||||
const char* comm_lib = GetEnv("CUDSS_COMM_LIB");
|
||||
#ifdef MFEM_CUDSS_COMM_LIB
|
||||
if (comm_lib == nullptr)
|
||||
{
|
||||
comm_lib = MFEM_CUDSS_COMM_LIB;
|
||||
}
|
||||
#endif
|
||||
MFEM_CUDSS_CHECK(cudssSetCommLayer(handle, comm_lib));
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM,
|
||||
&mpi_comm, sizeof(MPI_Comm *)));
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
CuDSSSolver::~CuDSSSolver()
|
||||
{
|
||||
// Destroy the system Matrix, RHS vector and solution vector
|
||||
if (Ac)
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
|
||||
}
|
||||
|
||||
// Destroy the cuDSS handle, solver config and solver data
|
||||
MFEM_CUDSS_CHECK(cudssDataDestroy(handle, solverData));
|
||||
MFEM_CUDSS_CHECK(cudssConfigDestroy(solverConfig));
|
||||
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssDestroy(handle));
|
||||
handle = nullptr;
|
||||
|
||||
|
||||
if (csr_offsets_d != NULL)
|
||||
{
|
||||
CuMemFree(csr_offsets_d);
|
||||
}
|
||||
|
||||
if (csr_columns_d != NULL)
|
||||
{
|
||||
CuMemFree(csr_columns_d);
|
||||
}
|
||||
|
||||
if (csr_values_d != NULL)
|
||||
{
|
||||
CuMemFree(csr_values_d);
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::InitCuDSS()
|
||||
{
|
||||
// Create the cuDSS handle
|
||||
MFEM_CUDSS_CHECK(cudssCreate(&handle));
|
||||
|
||||
#ifdef MFEM_USE_OPENMP
|
||||
// NOTE: Set the threading layer library name to NULL so that cuDSS picks
|
||||
// it from the environment variable "CUDSS_THREADING_LIB"
|
||||
const char* threading_lib = GetEnv("CUDSS_THREADING_LIB");
|
||||
#ifdef MFEM_CUDSS_THREADING_LIB
|
||||
if (threading_lib == nullptr)
|
||||
{
|
||||
threading_lib = MFEM_CUDSS_THREADING_LIB;
|
||||
}
|
||||
#endif
|
||||
MFEM_CUDSS_CHECK(cudssSetThreadingLayer(handle, threading_lib));
|
||||
#endif // MFEM_USE_OPENMP
|
||||
|
||||
// Create the solver configuration and data objects
|
||||
MFEM_CUDSS_CHECK(cudssConfigCreate(&solverConfig));
|
||||
MFEM_CUDSS_CHECK(cudssDataCreate(handle, &solverData));
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetMatrixSymType(MatType mtype_)
|
||||
{
|
||||
switch (mtype_)
|
||||
{
|
||||
case MatType::SYMMETRIC_INDEFINITE:
|
||||
mat_type = CUDSS_MTYPE_SYMMETRIC;
|
||||
break;
|
||||
case MatType::SYMMETRIC_POSITIVE_DEFINITE:
|
||||
mat_type = CUDSS_MTYPE_SPD;
|
||||
break;
|
||||
default:
|
||||
mat_type = CUDSS_MTYPE_GENERAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetMatrixViewType(MatViewType mvtype_)
|
||||
{
|
||||
// If the MatType is NONSYMMETRIC, the matrix view type must be FULL.
|
||||
if (mat_type == CUDSS_MTYPE_GENERAL)
|
||||
{
|
||||
mview = CUDSS_MVIEW_FULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the matrix is symmetric, the following view type will be optional.
|
||||
switch (mvtype_)
|
||||
{
|
||||
case MatViewType::LOWER:
|
||||
mview = CUDSS_MVIEW_LOWER;
|
||||
break;
|
||||
case MatViewType::UPPER:
|
||||
mview = CUDSS_MVIEW_UPPER;
|
||||
break;
|
||||
default:
|
||||
mview = CUDSS_MVIEW_FULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetReorderingReuse(bool reuse)
|
||||
{
|
||||
MFEM_VERIFY(Ac == nullptr,
|
||||
"Set reordering reuse before setting the operator!");
|
||||
reorder_reuse = reuse;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void CuDSSSolver::SetMatrix(const HypreParMatrix &op)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
|
||||
hypre_ParCSRMatrix *parcsr_op = op;
|
||||
op.HypreRead();
|
||||
hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op);
|
||||
op.HypreRead();
|
||||
#if MFEM_HYPRE_VERSION >= 21600
|
||||
hypre_CSRMatrixBigJtoJ(csr_op);
|
||||
#endif
|
||||
|
||||
// Parameters of the Operator
|
||||
n_loc = height; // Equal to the csr_op->num_rows
|
||||
n_global = internal::to_int(parcsr_op->global_num_rows);
|
||||
row_start = parcsr_op->first_row_index;
|
||||
row_end = row_start + n_loc - 1;
|
||||
MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
|
||||
(reorder_reuse && (nnz == csr_op->num_nonzeros)),
|
||||
"Inconsistent new matrix pattern!");
|
||||
nnz = csr_op->num_nonzeros;
|
||||
|
||||
SetMatrixCuDSS(csr_op->i, csr_op->j, csr_op->data);
|
||||
hypre_CSRMatrixDestroy(csr_op);
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
void CuDSSSolver::SetMatrix(const SparseMatrix &op)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
|
||||
// Parameters of the Operator
|
||||
MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
|
||||
(reorder_reuse && (nnz == op.NumNonZeroElems())),
|
||||
"Inconsistent new matrix pattern!");
|
||||
|
||||
SparseMatrix *A = const_cast<SparseMatrix *>(&op);
|
||||
|
||||
nnz = A->NumNonZeroElems();
|
||||
n_global = height; // Equal to the height in serial
|
||||
n_loc = height; // Equal to the height in serial
|
||||
|
||||
int *csr_offsets = const_cast<int *>(A->ReadI());
|
||||
int *csr_columns = const_cast<int *>(A->ReadJ());
|
||||
real_t *csr_values = const_cast<real_t *>(A->ReadData());
|
||||
|
||||
SetMatrixCuDSS(csr_offsets, csr_columns, csr_values);
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
|
||||
real_t *csr_values)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
// Initial the cudssMatrix objects
|
||||
if (!cuDSSObjectInitialized)
|
||||
{
|
||||
// Set the cudssMatrix object of csr operator
|
||||
Ac = std::make_unique<cudssMatrix_t>();
|
||||
// Create empty RHS and solution vectors
|
||||
SetNumRHS(1);
|
||||
// Allocate device memory for csr values
|
||||
CuMemAlloc(&csr_values_d, nnz * sizeof(real_t));
|
||||
}
|
||||
|
||||
CuMemcpyDtoD(csr_values_d, csr_values, nnz * sizeof(real_t));
|
||||
|
||||
// New cuDSS CSR matrix object and analysis or reuse the one from a previous
|
||||
// matrix
|
||||
if (!cuDSSObjectInitialized || !reorder_reuse)
|
||||
{
|
||||
if (reorder_reuse) // !cuDSSObjectInitialized && reorder_reuse
|
||||
{
|
||||
// NOTE: For CuDSS solver to reuse the reordering (skipping analysis
|
||||
// phase), it needs to access the I and J arrays of the **initial**
|
||||
// matrix. Therefore, we need to copy and keep I and J in device memory.
|
||||
CuMemAlloc(&csr_offsets_d, (n_loc + 1) * sizeof(int));
|
||||
CuMemAlloc(&csr_columns_d, nnz * sizeof(int));
|
||||
|
||||
CuMemcpyDtoD(csr_offsets_d, csr_offsets, (n_loc + 1) * sizeof(int));
|
||||
CuMemcpyDtoD(csr_columns_d, csr_columns, nnz * sizeof(int));
|
||||
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
|
||||
csr_columns_d, csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
|
||||
CUDSS_BASE_ZERO));
|
||||
}
|
||||
else // !reorder_reuse
|
||||
{
|
||||
if (cuDSSObjectInitialized)
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
|
||||
}
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL, csr_columns,
|
||||
csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
|
||||
CUDSS_BASE_ZERO));
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Mpi::IsInitialized())
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(*Ac, row_start, row_end));
|
||||
}
|
||||
#endif
|
||||
// Analysis
|
||||
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_ANALYSIS, solverConfig,
|
||||
solverData, *Ac, yc, xc));
|
||||
}
|
||||
else // cuDSSObjectInitialized && reorder_reuse
|
||||
{
|
||||
// NOTE: When reusing analysis result, we only update the Data array,
|
||||
// without changing the I and J arrays.
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetValues(*Ac, csr_values_d));
|
||||
}
|
||||
|
||||
// Factorization
|
||||
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_FACTORIZATION, solverConfig,
|
||||
solverData, *Ac, yc, xc));
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetOperator(const Operator &op)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
MFEM_VERIFY(
|
||||
!cuDSSObjectInitialized || (height == op.Height() && width == op.Width()),
|
||||
"Inconsistent new matrix size!");
|
||||
height = op.Height();
|
||||
width = op.Width();
|
||||
if (const SparseMatrix *A = dynamic_cast<const SparseMatrix *>(&op))
|
||||
{
|
||||
SetMatrix(*A);
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
else if (const HypreParMatrix *A =
|
||||
dynamic_cast<const HypreParMatrix *>(&op))
|
||||
{
|
||||
SetMatrix(*A);
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported Operator Type \n");
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetNumRHS(int nrhs_) const
|
||||
{
|
||||
if (nrhs != nrhs_)
|
||||
{
|
||||
if (nrhs > 0)
|
||||
{
|
||||
// Destroy the previous RHS vector and solution vector
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
|
||||
}
|
||||
// Create empty RHS and solution vectors
|
||||
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&xc, n_global, nrhs_, n_global, NULL,
|
||||
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&yc, n_global, nrhs_, n_global, NULL,
|
||||
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(xc, row_start, row_end));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(yc, row_start, row_end));
|
||||
#endif // MFEM_USE_MPI
|
||||
}
|
||||
nrhs = nrhs_;
|
||||
}
|
||||
|
||||
void CuDSSSolver::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
Array<const Vector *> X(1);
|
||||
Array<Vector *> Y(1);
|
||||
X[0] = &x;
|
||||
Y[0] = &y;
|
||||
ArrayMult(X, Y);
|
||||
}
|
||||
|
||||
void CuDSSSolver::ArrayMult(const Array<const Vector *> &X,
|
||||
Array<Vector *> &Y) const
|
||||
{
|
||||
SetNumRHS(X.Size());
|
||||
|
||||
Vector RHS, SOL;
|
||||
|
||||
if (nrhs == 1)
|
||||
{
|
||||
RHS.MakeRef(*(const_cast<Vector *>(X[0])), 0, X[0]->Size());
|
||||
SOL.MakeRef(*Y[0], 0, Y[0]->Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: RHS must have **global** num_rows and nrhs columns
|
||||
RHS.SetSize(nrhs * n_global, *X[0]);
|
||||
for (int i = 0; i < nrhs; i++)
|
||||
{
|
||||
Vector s(RHS, i * n_global, n_loc);
|
||||
s = *X[i];
|
||||
}
|
||||
|
||||
// NOTE: SOL must have **global** num_rows and nrhs columns
|
||||
SOL.SetSize(nrhs * n_global, *Y[0]);
|
||||
}
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetValues(xc, const_cast<real_t *>(RHS.Read())));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetValues(yc, SOL.Write()));
|
||||
|
||||
// Solve
|
||||
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_SOLVE, solverConfig,
|
||||
solverData, *Ac, yc, xc));
|
||||
|
||||
if (nrhs == 1)
|
||||
{
|
||||
SOL.SyncAliasMemory(*Y[0]);
|
||||
}
|
||||
|
||||
if (nrhs > 1)
|
||||
{
|
||||
// Get solution for each right-hand side
|
||||
for (int i = 0; i < nrhs; i++)
|
||||
{
|
||||
Vector s(SOL, i * n_global, n_loc);
|
||||
*Y[i] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
#endif // MFEM_USE_CUDSS
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_CUDSS
|
||||
#define MFEM_CUDSS
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
|
||||
#include "cudss.h"
|
||||
#include <memory>
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include <mpi.h>
|
||||
#include "hypre.hpp"
|
||||
#else
|
||||
#include "operator.hpp"
|
||||
#include "sparsemat.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/**
|
||||
* @brief cuDSS: A high-performance CUDA Library for Direct Sparse Solvers
|
||||
*
|
||||
* Interface for the distributed cuDSS solver
|
||||
*/
|
||||
class CuDSSSolver : public Solver
|
||||
{
|
||||
public:
|
||||
/// Specify the type of matrix we are applying the solver to
|
||||
enum MatType
|
||||
{
|
||||
/// CUDSS_MTYPE_GENERAL: General matrix [default].
|
||||
NONSYMMETRIC = 0,
|
||||
/// CUDSS_MTYPE_SYMMETRIC: Real symmetric matrix.
|
||||
SYMMETRIC_INDEFINITE = 1,
|
||||
/// CUDSS_MTYPE_SPD: Symmetric positive-definite matrix.
|
||||
SYMMETRIC_POSITIVE_DEFINITE = 2,
|
||||
};
|
||||
|
||||
/// Specify the view type of matrix we are applying the solver to
|
||||
enum MatViewType
|
||||
{
|
||||
/// CUDSS_MVIEW_FULL: Full matrix [default]
|
||||
FULL = 0,
|
||||
/// CUDSS_MVIEW_LOWER: Lower-triangular matrix (including the diagonal).
|
||||
LOWER = 1,
|
||||
/// CUDSS_MVIEW_UPPER: Upper-triangular matrix (including the diagonal).
|
||||
UPPER = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*/
|
||||
CuDSSSolver();
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/**
|
||||
* @brief Constructor with MPI_Comm parameter.
|
||||
*/
|
||||
CuDSSSolver(MPI_Comm comm);
|
||||
#endif
|
||||
|
||||
// Note: CuDSSSolver disables the move copy constructor and move assignment
|
||||
// operator
|
||||
CuDSSSolver(CuDSSSolver &&) = delete;
|
||||
CuDSSSolver &operator=(CuDSSSolver &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Set the matrix type
|
||||
*
|
||||
* Supported matrix types:
|
||||
* CuDSSSolver::NONSYMMETRIC,
|
||||
* CuDSSSolver::SYMMETRIC_INDEFINITE,
|
||||
* and CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE
|
||||
*
|
||||
* @param mtype_ Matrix type
|
||||
*
|
||||
* @note This method has to be called before SetOperator
|
||||
*/
|
||||
void SetMatrixSymType(MatType mtype_);
|
||||
|
||||
/**
|
||||
* @brief Set the matrix view type
|
||||
*
|
||||
* Supported matrix types:
|
||||
* CuDSSSolver::FULL,
|
||||
* CuDSSSolver::LOWER,
|
||||
* and CuDSSSolver::UPPER
|
||||
*
|
||||
* @param mvtype Matrix view type
|
||||
*
|
||||
* @note This method has to be called before SetOperator
|
||||
*/
|
||||
void SetMatrixViewType(MatViewType mvtype);
|
||||
|
||||
/**
|
||||
* @brief Set the flag controlling reuse of the symbolic factorization
|
||||
* for multiple operators
|
||||
*
|
||||
* @param reuse Flag to reuse symbolic factorization
|
||||
*
|
||||
* @note This method has to be called before repeated calls to SetOperator
|
||||
*/
|
||||
void SetReorderingReuse(bool reuse);
|
||||
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
/**
|
||||
* @brief Solve $ y = Op^{-1} x $
|
||||
*
|
||||
* @param x RHS vector
|
||||
* @param y Solution vector
|
||||
*/
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/**
|
||||
* @brief Solve $ Y_i = Op^{-1} X_i $
|
||||
*
|
||||
* @param X Array of RHS vectors
|
||||
* @param Y Array of Solution vectors
|
||||
*/
|
||||
void ArrayMult(const Array<const Vector *> &X,
|
||||
Array<Vector *> &Y) const override;
|
||||
|
||||
~CuDSSSolver();
|
||||
|
||||
private:
|
||||
#ifdef MFEM_USE_MPI
|
||||
// MPI_Comm
|
||||
MPI_Comm mpi_comm = MPI_COMM_NULL;
|
||||
|
||||
int row_start = 0; // the first row index in CSR matrix operator
|
||||
int row_end = 0; // the end row index in CSR matrix operator
|
||||
#endif
|
||||
|
||||
// Parameter controlling whether or not to reuse the symbolic factorization
|
||||
// for multiple calls to SetOperator
|
||||
bool reorder_reuse = false;
|
||||
|
||||
// Parameter controlling the matrix type
|
||||
cudssMatrixType_t mat_type = CUDSS_MTYPE_GENERAL;
|
||||
|
||||
int n_global = 0; // global number of rows
|
||||
int n_loc = 0; // the number of the rows in CSR matrix operator
|
||||
|
||||
mutable int nrhs = 0; // the number of the RHSs
|
||||
int nnz = 0; // the number of non zeros
|
||||
|
||||
// copy and keep the I and J arrays in device memory when skipping analysis
|
||||
// phase
|
||||
void *csr_offsets_d = NULL; // copy and keep I in device
|
||||
void *csr_columns_d = NULL; // copy and keep J in device
|
||||
void *csr_values_d = NULL; // copy and keep csr data in device
|
||||
|
||||
// cuDSS object specifies available matrix types for sparse matrices
|
||||
cudssMatrixViewType_t mview = CUDSS_MVIEW_FULL;
|
||||
|
||||
// cuDSS objects storage for sparse matrix Ac, RHS yc and solution xc
|
||||
std::unique_ptr<cudssMatrix_t> Ac;
|
||||
mutable cudssMatrix_t xc, yc;
|
||||
|
||||
// common for all cuDSS solver instances.
|
||||
// cuDSS object holds the cuDSS library context
|
||||
cudssHandle_t handle;
|
||||
|
||||
// cuDSS object stores configuration settings for the solver
|
||||
mutable cudssConfig_t solverConfig;
|
||||
// cuDSS object holds internal data
|
||||
mutable cudssData_t solverData;
|
||||
|
||||
/// Method for configuring storage for distributed/centralized RHS and
|
||||
/// solution
|
||||
void SetNumRHS(int nrhs_) const;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/**
|
||||
* @brief Set the HypreParMatrix object
|
||||
*
|
||||
* @param op HypreParMatrix object
|
||||
*
|
||||
* @note This method is called inside SetOperator
|
||||
*/
|
||||
void SetMatrix(const HypreParMatrix &op);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set the SparseMatrix object
|
||||
*
|
||||
* @param op SparseMatrix object
|
||||
*
|
||||
* @note This method is called inside SetOperator
|
||||
*/
|
||||
void SetMatrix(const SparseMatrix &op);
|
||||
|
||||
/**
|
||||
* @brief Set the matrix values for cuDSS
|
||||
*
|
||||
* @param csr_offsets Row offsets of the CSR matrix
|
||||
* @param csr_columns Column indices of the CSR matrix
|
||||
* @param csr_values Non-zero values of the CSR matrix
|
||||
*
|
||||
* @note This method is called inside SetMatrix.
|
||||
*/
|
||||
void SetMatrixCuDSS(int* csr_offsets, int* csr_columns, real_t* csr_values);
|
||||
|
||||
/// Method for initializing the cuDSS library and creating the cuDSS handle
|
||||
void InitCuDSS();
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_CUDSS
|
||||
#endif // MFEM_CUDSS
|
||||
@@ -91,4 +91,8 @@
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
#include "cudss.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+3
-237
@@ -20,9 +20,7 @@
|
||||
#include "../fem/fem.hpp"
|
||||
|
||||
#include "petsc.h"
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
#include "petscmathypre.h"
|
||||
#endif
|
||||
|
||||
// Backward compatibility
|
||||
#if PETSC_VERSION_LT(3,11,0)
|
||||
@@ -105,14 +103,6 @@ static PetscErrorCode MakeShellPC(PC,mfem::Solver&,bool);
|
||||
static PetscErrorCode MakeShellPCWithFactory(PC,
|
||||
mfem::PetscPreconditionerFactory*);
|
||||
|
||||
// Equivalent functions are present in PETSc source code
|
||||
// if PETSc has been compiled with hypre support
|
||||
// We provide them here in case PETSC_HAVE_HYPRE is not defined
|
||||
#if !defined(PETSC_HAVE_HYPRE)
|
||||
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix*,Mat*);
|
||||
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix*,Mat*);
|
||||
#endif
|
||||
|
||||
#if PETSC_VERSION_GE(3,15,0) && defined(PETSC_HAVE_DEVICE)
|
||||
#if defined(MFEM_USE_CUDA) && defined(PETSC_HAVE_CUDA)
|
||||
#define _USE_DEVICE
|
||||
@@ -1149,11 +1139,10 @@ PetscParMatrix& PetscParMatrix::operator=(const HypreParMatrix& B)
|
||||
}
|
||||
height = B.Height();
|
||||
width = B.Width();
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
|
||||
ierr = MatCreateFromParCSR(B,MATAIJ,PETSC_USE_POINTER,&A);
|
||||
#else
|
||||
ierr = MatConvert_hypreParCSR_AIJ(B,&A); CCHKERRQ(B.GetComm(),ierr);
|
||||
#endif
|
||||
CCHKERRQ(B.GetComm(),ierr);
|
||||
|
||||
SetUpForDevice();
|
||||
return *this;
|
||||
}
|
||||
@@ -1529,7 +1518,6 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
}
|
||||
else if (tid == PETSC_MATHYPRE)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
if (istrans)
|
||||
{
|
||||
Mat B;
|
||||
@@ -1541,9 +1529,6 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
{
|
||||
ierr = MatConvert(pA->A,MATHYPRE,MAT_INITIAL_MATRIX,A); PCHKERRQ(pA->A,ierr);
|
||||
}
|
||||
#else
|
||||
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
|
||||
#endif
|
||||
}
|
||||
else if (tid == PETSC_MATSHELL)
|
||||
{
|
||||
@@ -1558,33 +1543,21 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
{
|
||||
if (tid == PETSC_MATAIJ)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATAIJ,
|
||||
PETSC_USE_POINTER,A);
|
||||
#else
|
||||
ierr = MatConvert_hypreParCSR_AIJ(const_cast<HypreParMatrix&>(*pH),A);
|
||||
#endif
|
||||
CCHKERRQ(pH->GetComm(),ierr);
|
||||
}
|
||||
else if (tid == PETSC_MATIS)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATIS,
|
||||
PETSC_USE_POINTER,A);
|
||||
#else
|
||||
ierr = MatConvert_hypreParCSR_IS(const_cast<HypreParMatrix&>(*pH),A);
|
||||
#endif
|
||||
CCHKERRQ(pH->GetComm(),ierr);
|
||||
}
|
||||
else if (tid == PETSC_MATHYPRE || tid == ANY_TYPE)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATHYPRE,
|
||||
PETSC_USE_POINTER,A);
|
||||
CCHKERRQ(pH->GetComm(),ierr);
|
||||
#else
|
||||
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
|
||||
#endif
|
||||
}
|
||||
else if (tid == PETSC_MATSHELL)
|
||||
{
|
||||
@@ -2238,11 +2211,7 @@ PetscParMatrix * RAP(PetscParMatrix *A, PetscParMatrix *P)
|
||||
PetscParMatrix * RAP(HypreParMatrix *hA, PetscParMatrix *P)
|
||||
{
|
||||
PetscParMatrix *out,*A;
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
A = new PetscParMatrix(hA,Operator::PETSC_MATHYPRE);
|
||||
#else
|
||||
A = new PetscParMatrix(hA);
|
||||
#endif
|
||||
out = RAP(P,A,P);
|
||||
delete A;
|
||||
return out;
|
||||
@@ -2349,10 +2318,8 @@ Operator::Type PetscParMatrix::GetType() const
|
||||
if (ok == PETSC_TRUE) { return PETSC_MATSHELL; }
|
||||
ierr = PetscObjectTypeCompare(oA, MATNEST, &ok); PCHKERRQ(A,ierr);
|
||||
if (ok == PETSC_TRUE) { return PETSC_MATNEST; }
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = PetscObjectTypeCompare(oA, MATHYPRE, &ok); PCHKERRQ(A,ierr);
|
||||
if (ok == PETSC_TRUE) { return PETSC_MATHYPRE; }
|
||||
#endif
|
||||
return PETSC_MATGENERIC;
|
||||
}
|
||||
|
||||
@@ -5527,207 +5494,6 @@ static PetscErrorCode Convert_Vmarks_IS(MPI_Comm comm,
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#if !defined(PETSC_HAVE_HYPRE)
|
||||
|
||||
#if defined(HYPRE_MIXEDINT)
|
||||
#error "HYPRE_MIXEDINT not supported"
|
||||
#endif
|
||||
|
||||
#include "_hypre_parcsr_mv.h"
|
||||
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
{
|
||||
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
|
||||
hypre_CSRMatrix *hdiag,*hoffd;
|
||||
PetscScalar *da,*oa,*aptr;
|
||||
PetscInt *dii,*djj,*oii,*ojj,*iptr;
|
||||
PetscInt i,dnnz,onnz,m,n;
|
||||
PetscMPIInt size;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
hdiag = hypre_ParCSRMatrixDiag(hA);
|
||||
hoffd = hypre_ParCSRMatrixOffd(hA);
|
||||
m = hypre_CSRMatrixNumRows(hdiag);
|
||||
n = hypre_CSRMatrixNumCols(hdiag);
|
||||
dnnz = hypre_CSRMatrixNumNonzeros(hdiag);
|
||||
onnz = hypre_CSRMatrixNumNonzeros(hoffd);
|
||||
ierr = PetscMalloc1(m+1,&dii); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(dnnz,&djj); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(dnnz,&da); CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(dii,hypre_CSRMatrixI(hdiag),(m+1)*sizeof(PetscInt));
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(djj,hypre_CSRMatrixJ(hdiag),dnnz*sizeof(PetscInt));
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(da,hypre_CSRMatrixData(hdiag),dnnz*sizeof(PetscScalar));
|
||||
CHKERRQ(ierr);
|
||||
iptr = djj;
|
||||
aptr = da;
|
||||
for (i=0; i<m; i++)
|
||||
{
|
||||
PetscInt nc = dii[i+1]-dii[i];
|
||||
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
|
||||
iptr += nc;
|
||||
aptr += nc;
|
||||
}
|
||||
ierr = MPI_Comm_size(comm,&size); CHKERRQ(ierr);
|
||||
if (size > 1)
|
||||
{
|
||||
PetscInt *offdj,*coffd;
|
||||
|
||||
ierr = PetscMalloc1(m+1,&oii); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(onnz,&ojj); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(onnz,&oa); CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(oii,hypre_CSRMatrixI(hoffd),(m+1)*sizeof(PetscInt));
|
||||
CHKERRQ(ierr);
|
||||
offdj = hypre_CSRMatrixJ(hoffd);
|
||||
coffd = hypre_ParCSRMatrixColMapOffd(hA);
|
||||
for (i=0; i<onnz; i++) { ojj[i] = coffd[offdj[i]]; }
|
||||
ierr = PetscMemcpy(oa,hypre_CSRMatrixData(hoffd),onnz*sizeof(PetscScalar));
|
||||
CHKERRQ(ierr);
|
||||
iptr = ojj;
|
||||
aptr = oa;
|
||||
for (i=0; i<m; i++)
|
||||
{
|
||||
PetscInt nc = oii[i+1]-oii[i];
|
||||
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
|
||||
iptr += nc;
|
||||
aptr += nc;
|
||||
}
|
||||
ierr = MatCreateMPIAIJWithSplitArrays(comm,m,n,PETSC_DECIDE,PETSC_DECIDE,dii,
|
||||
djj,da,oii,ojj,oa,pA); CHKERRQ(ierr);
|
||||
}
|
||||
else
|
||||
{
|
||||
oii = ojj = NULL;
|
||||
oa = NULL;
|
||||
ierr = MatCreateSeqAIJWithArrays(comm,m,n,dii,djj,da,pA); CHKERRQ(ierr);
|
||||
}
|
||||
/* We are responsible to free the CSR arrays. However, since we can take
|
||||
references of a PetscParMatrix but we cannot take reference of PETSc
|
||||
arrays, we need to create a PetscContainer object to take reference of
|
||||
these arrays in reference objects */
|
||||
void *ptrs[6] = {dii,djj,da,oii,ojj,oa};
|
||||
const char *names[6] = {"_mfem_csr_dii",
|
||||
"_mfem_csr_djj",
|
||||
"_mfem_csr_da",
|
||||
"_mfem_csr_oii",
|
||||
"_mfem_csr_ojj",
|
||||
"_mfem_csr_oa"
|
||||
};
|
||||
for (i=0; i<6; i++)
|
||||
{
|
||||
PetscContainer c;
|
||||
|
||||
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
|
||||
}
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
{
|
||||
Mat lA;
|
||||
ISLocalToGlobalMapping rl2g,cl2g;
|
||||
IS is;
|
||||
hypre_CSRMatrix *hdiag,*hoffd;
|
||||
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
|
||||
void *ptrs[2];
|
||||
const char *names[2] = {"_mfem_csr_aux",
|
||||
"_mfem_csr_data"
|
||||
};
|
||||
PetscScalar *hdd,*hod,*aa,*data;
|
||||
PetscInt *col_map_offd,*hdi,*hdj,*hoi,*hoj;
|
||||
PetscInt *aux,*ii,*jj;
|
||||
PetscInt cum,dr,dc,oc,str,stc,nnz,i,jd,jo;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
/* access relevant information in ParCSR */
|
||||
str = hypre_ParCSRMatrixFirstRowIndex(hA);
|
||||
stc = hypre_ParCSRMatrixFirstColDiag(hA);
|
||||
hdiag = hypre_ParCSRMatrixDiag(hA);
|
||||
hoffd = hypre_ParCSRMatrixOffd(hA);
|
||||
dr = hypre_CSRMatrixNumRows(hdiag);
|
||||
dc = hypre_CSRMatrixNumCols(hdiag);
|
||||
nnz = hypre_CSRMatrixNumNonzeros(hdiag);
|
||||
hdi = hypre_CSRMatrixI(hdiag);
|
||||
hdj = hypre_CSRMatrixJ(hdiag);
|
||||
hdd = hypre_CSRMatrixData(hdiag);
|
||||
oc = hypre_CSRMatrixNumCols(hoffd);
|
||||
nnz += hypre_CSRMatrixNumNonzeros(hoffd);
|
||||
hoi = hypre_CSRMatrixI(hoffd);
|
||||
hoj = hypre_CSRMatrixJ(hoffd);
|
||||
hod = hypre_CSRMatrixData(hoffd);
|
||||
|
||||
/* generate l2g maps for rows and cols */
|
||||
ierr = ISCreateStride(comm,dr,str,1,&is); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingCreateIS(is,&rl2g); CHKERRQ(ierr);
|
||||
ierr = ISDestroy(&is); CHKERRQ(ierr);
|
||||
col_map_offd = hypre_ParCSRMatrixColMapOffd(hA);
|
||||
ierr = PetscMalloc1(dc+oc,&aux); CHKERRQ(ierr);
|
||||
for (i=0; i<dc; i++) { aux[i] = i+stc; }
|
||||
for (i=0; i<oc; i++) { aux[i+dc] = col_map_offd[i]; }
|
||||
ierr = ISCreateGeneral(comm,dc+oc,aux,PETSC_OWN_POINTER,&is); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingCreateIS(is,&cl2g); CHKERRQ(ierr);
|
||||
ierr = ISDestroy(&is); CHKERRQ(ierr);
|
||||
|
||||
/* create MATIS object */
|
||||
ierr = MatCreate(comm,pA); CHKERRQ(ierr);
|
||||
ierr = MatSetSizes(*pA,dr,dc,PETSC_DECIDE,PETSC_DECIDE); CHKERRQ(ierr);
|
||||
ierr = MatSetType(*pA,MATIS); CHKERRQ(ierr);
|
||||
ierr = MatSetLocalToGlobalMapping(*pA,rl2g,cl2g); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingDestroy(&rl2g); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingDestroy(&cl2g); CHKERRQ(ierr);
|
||||
|
||||
/* merge local matrices */
|
||||
ierr = PetscMalloc1(nnz+dr+1,&aux); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(nnz,&data); CHKERRQ(ierr);
|
||||
ii = aux;
|
||||
jj = aux+dr+1;
|
||||
aa = data;
|
||||
*ii = *(hdi++) + *(hoi++);
|
||||
for (jd=0,jo=0,cum=0; *ii<nnz; cum++)
|
||||
{
|
||||
PetscScalar *aold = aa;
|
||||
PetscInt *jold = jj,nc = jd+jo;
|
||||
for (; jd<*hdi; jd++) { *jj++ = *hdj++; *aa++ = *hdd++; }
|
||||
for (; jo<*hoi; jo++) { *jj++ = *hoj++ + dc; *aa++ = *hod++; }
|
||||
*(++ii) = *(hdi++) + *(hoi++);
|
||||
ierr = PetscSortIntWithScalarArray(jd+jo-nc,jold,aold); CHKERRQ(ierr);
|
||||
}
|
||||
for (; cum<dr; cum++) { *(++ii) = nnz; }
|
||||
ii = aux;
|
||||
jj = aux+dr+1;
|
||||
aa = data;
|
||||
ierr = MatCreateSeqAIJWithArrays(PETSC_COMM_SELF,dr,dc+oc,ii,jj,aa,&lA);
|
||||
CHKERRQ(ierr);
|
||||
ptrs[0] = aux;
|
||||
ptrs[1] = data;
|
||||
for (i=0; i<2; i++)
|
||||
{
|
||||
PetscContainer c;
|
||||
|
||||
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
|
||||
}
|
||||
ierr = MatISSetLocalMat(*pA,lA); CHKERRQ(ierr);
|
||||
ierr = MatDestroy(&lA); CHKERRQ(ierr);
|
||||
ierr = MatAssemblyBegin(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
||||
ierr = MatAssemblyEnd(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <petsc/private/matimpl.h>
|
||||
|
||||
static PetscErrorCode __mfem_MatCreateDummy(MPI_Comm comm, PetscInt m,
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#if !defined(PETSC_USE_64BIT_INDICES) && (defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
|
||||
#error "Mismatch between HYPRE (64bit) and PETSc (32bit) integer types"
|
||||
#endif
|
||||
#if !defined(PETSC_HAVE_HYPRE)
|
||||
#error "MFEM requires PETSc built with HYPRE support"
|
||||
#endif
|
||||
|
||||
#include "petscversion.h"
|
||||
#if PETSC_VERSION_GE(3,12,0)
|
||||
|
||||
@@ -307,7 +307,7 @@ endif
|
||||
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
|
||||
SUITESPARSE STRUMPACK GINKGO GNUTLS HDF5 NETCDF SLEPC PETSC MPFR PUMI HIOP\
|
||||
GSLIB OCCA CEED RAJA UMPIRE MKL_CPARDISO MKL_PARDISO AMGX MAGMA CALIPER PARELAG\
|
||||
TRIBOL BENCHMARK MOONOLITH ALGOIM
|
||||
TRIBOL BENCHMARK MOONOLITH ALGOIM CUDSS
|
||||
|
||||
|
||||
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
|
||||
@@ -376,7 +376,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
|
||||
MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO MFEM_USE_AMGX\
|
||||
MFEM_USE_MAGMA MFEM_USE_MUMPS MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER\
|
||||
MFEM_USE_BENCHMARK MFEM_USE_PARELAG MFEM_USE_TRIBOL MFEM_USE_ALGOIM MFEM_USE_ENZYME\
|
||||
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE
|
||||
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE\
|
||||
MFEM_USE_CUDSS MFEM_CUDSS_COMM_LIB MFEM_CUDSS_THREADING_LIB
|
||||
|
||||
# List of makefile variables that will be written to config.mk:
|
||||
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
|
||||
@@ -411,7 +412,7 @@ MFEM_INSTALL_DIR = $(abspath $(MFEM_PREFIX))
|
||||
# If we have 'config' target, export variables used by config/makefile
|
||||
ifneq (,$(filter config,$(MAKECMDGOALS)))
|
||||
export $(MFEM_DEFINES) MFEM_DEFINES $(MFEM_CONFIG_VARS) MFEM_CONFIG_VARS
|
||||
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT
|
||||
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT CUDSS_OPT
|
||||
endif
|
||||
|
||||
# If we have 'install' target, export variables used by config/makefile
|
||||
@@ -742,6 +743,7 @@ status info:
|
||||
$(info MFEM_USE_SUPERLU5 = $(MFEM_USE_SUPERLU5))
|
||||
$(info MFEM_USE_MUMPS = $(MFEM_USE_MUMPS))
|
||||
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
|
||||
$(info MFEM_USE_CUDSS = $(MFEM_USE_CUDSS))
|
||||
$(info MFEM_USE_GINKGO = $(MFEM_USE_GINKGO))
|
||||
$(info MFEM_USE_AMGX = $(MFEM_USE_AMGX))
|
||||
$(info MFEM_USE_MAGMA = $(MFEM_USE_MAGMA))
|
||||
|
||||
@@ -203,6 +203,69 @@ void Mesh::GetBoundingBox(Vector &min, Vector &max, int ref)
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::GetElementBoundingBoxes(Vector &elmin, Vector &elmax, int nref)
|
||||
{
|
||||
int nel = GetNE();
|
||||
elmin.SetSize(spaceDim * nel);
|
||||
elmax.SetSize(spaceDim * nel);
|
||||
elmin = numeric_limits<real_t>::max();
|
||||
elmax = -numeric_limits<real_t>::max();
|
||||
if (Nodes == NULL)
|
||||
{
|
||||
Array<int> verts;
|
||||
real_t *coord;
|
||||
// create bounding boxes from vertex coordinates
|
||||
for (int e = 0; e < nel; e++)
|
||||
{
|
||||
GetElementVertices(e, verts);
|
||||
for (int v = 0; v < verts.Size(); v++)
|
||||
{
|
||||
coord = GetVertex(verts[v]);
|
||||
for (int d = 0; d < spaceDim; d++)
|
||||
{
|
||||
elmin(d*nel + e) = min(elmin(d*nel + e), coord[d]);
|
||||
elmax(d*nel + e) = max(elmax(d*nel + e), coord[d]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Nodes->GetElementBounds(elmin, elmax, nref);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::GetElementBoundingBox(int elem, Vector &elmin, Vector &elmax,
|
||||
int nref)
|
||||
{
|
||||
elmin.SetSize(spaceDim);
|
||||
elmax.SetSize(spaceDim);
|
||||
elmin = numeric_limits<real_t>::max();
|
||||
elmax = -numeric_limits<real_t>::max();
|
||||
if (Nodes == NULL)
|
||||
{
|
||||
Array<int> verts;
|
||||
real_t *coord;
|
||||
// create bounding boxes from vertex coordinates
|
||||
GetElementVertices(elem, verts);
|
||||
for (int v = 0; v < verts.Size(); v++)
|
||||
{
|
||||
coord = GetVertex(verts[v]);
|
||||
for (int d = 0; d < spaceDim; d++)
|
||||
{
|
||||
elmin(d) = min(elmin(d), coord[d]);
|
||||
elmax(d) = max(elmax(d), coord[d]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int max_order = Nodes->FESpace()->GetMaxElementOrder();
|
||||
PLBound plb(Nodes->FESpace(), nref*(max_order+1));
|
||||
Nodes->GetElementBounds(elem, plb, elmin, elmax);
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::GetCharacteristics(real_t &h_min, real_t &h_max,
|
||||
real_t &kappa_min, real_t &kappa_max,
|
||||
Vector *Vh, Vector *Vk)
|
||||
|
||||
@@ -1366,6 +1366,11 @@ public:
|
||||
/** For high-order meshes, the geometry is first refined @a ref times. */
|
||||
void GetBoundingBox(Vector &min, Vector &max, int ref = 2);
|
||||
|
||||
/// Returns the minimum and maximum corners of the element bounding box.
|
||||
void GetElementBoundingBoxes(Vector &elmin, Vector &elmax, int nref=2);
|
||||
|
||||
void GetElementBoundingBox(int elem, Vector &elmin, Vector &elmax, int nref=2);
|
||||
|
||||
void GetCharacteristics(real_t &h_min, real_t &h_max,
|
||||
real_t &kappa_min, real_t &kappa_max,
|
||||
Vector *Vh = NULL, Vector *Vk = NULL);
|
||||
|
||||
+236
-38
@@ -4838,6 +4838,8 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
|
||||
{
|
||||
int shared_bdr_attr;
|
||||
Array<int> nc_shared_faces;
|
||||
Array<int> interface_faces;
|
||||
int interface_bdr_attr = 0;
|
||||
|
||||
if (NURBSext)
|
||||
{
|
||||
@@ -4882,6 +4884,19 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
|
||||
const bool set_names = attribute_sets.SetsExist() ||
|
||||
bdr_attribute_sets.SetsExist();
|
||||
|
||||
// Add material interfaces as boundary elements for visualization. We build a
|
||||
// list of local faces to print as extra boundary elements. This does not
|
||||
// modify the ParMesh object, only the printed mesh.
|
||||
if (print_interfaces && Dim > 1)
|
||||
{
|
||||
FindInterface(interface_faces);
|
||||
|
||||
// Choose a boundary attribute that does not collide with existing ones,
|
||||
// including those introduced by print_shared.
|
||||
const int max_bdr_attr = bdr_attributes.Size() ? bdr_attributes.Max() : 0;
|
||||
interface_bdr_attr = max_bdr_attr + 1 + (print_shared ? NRanks : 0);
|
||||
}
|
||||
|
||||
os << (!set_names ? "MFEM mesh v1.0\n" : "MFEM mesh v1.3\n");
|
||||
|
||||
if (!comments.empty()) { os << '\n' << comments << '\n'; }
|
||||
@@ -4916,6 +4931,11 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
|
||||
{
|
||||
num_bdr_elems += s2l_face->Size();
|
||||
}
|
||||
if (print_interfaces && Dim > 1)
|
||||
{
|
||||
// in 3D we print two oriented copies for each material interface face
|
||||
num_bdr_elems += (Dim == 3 ? 2 : 1) * interface_faces.Size();
|
||||
}
|
||||
os << "\nboundary\n" << num_bdr_elems << '\n';
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
@@ -4940,6 +4960,51 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
}
|
||||
|
||||
// Print interface faces as additional boundary elements. In 3D we print two
|
||||
// copies of each face, with opposite orientation, so material subdomains can
|
||||
// be pulled apart with F11/F12 in GLVis.
|
||||
if (print_interfaces && Dim > 1)
|
||||
{
|
||||
for (int i = 0; i < interface_faces.Size(); i++)
|
||||
{
|
||||
const int f = interface_faces[i];
|
||||
const int *fv = faces[f]->GetVertices();
|
||||
if (Dim == 2)
|
||||
{
|
||||
Segment seg(fv, interface_bdr_attr);
|
||||
PrintElement(&seg, os);
|
||||
}
|
||||
else // Dim == 3
|
||||
{
|
||||
const Geometry::Type geom = faces[f]->GetGeometryType();
|
||||
if (geom == Geometry::TRIANGLE)
|
||||
{
|
||||
int v0[3] = { fv[0], fv[1], fv[2] };
|
||||
int v1[3] = { fv[0], fv[2], fv[1] };
|
||||
Triangle t0(v0, interface_bdr_attr);
|
||||
Triangle t1(v1, interface_bdr_attr);
|
||||
PrintElement(&t0, os);
|
||||
PrintElement(&t1, os);
|
||||
}
|
||||
else if (geom == Geometry::SQUARE)
|
||||
{
|
||||
int v0[4] = { fv[0], fv[1], fv[2], fv[3] };
|
||||
int v1[4] = { fv[0], fv[3], fv[2], fv[1] };
|
||||
Quadrilateral q0(v0, interface_bdr_attr);
|
||||
Quadrilateral q1(v1, interface_bdr_attr);
|
||||
PrintElement(&q0, os);
|
||||
PrintElement(&q1, os);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("unsupported 3D face geometry type '"
|
||||
<< Geometry::Name[geom]
|
||||
<< "' while printing interface boundaries.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (set_names)
|
||||
{
|
||||
os << "\nbdr_attribute_sets\n";
|
||||
@@ -4989,8 +5054,9 @@ void ParMesh::Print(adios2stream &os) const
|
||||
}
|
||||
#endif
|
||||
|
||||
static void dump_element(const Element* elem, Array<int> &data)
|
||||
static void dump_element_with_attr(const Element* elem, Array<int> &data)
|
||||
{
|
||||
data.Append(elem->GetAttribute());
|
||||
data.Append(elem->GetGeometryType());
|
||||
|
||||
int nv = elem->GetNVertices();
|
||||
@@ -5001,13 +5067,51 @@ static void dump_element(const Element* elem, Array<int> &data)
|
||||
}
|
||||
}
|
||||
|
||||
void ParMesh::FindInterface(Array<int> &interface) const
|
||||
{
|
||||
// We need face neighbor elements to determine if shared faces in parallel
|
||||
// are on material interfaces.
|
||||
const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
|
||||
|
||||
interface.SetSize(0); // clear 'interface' since we Append to it
|
||||
const int nf = GetNumFaces();
|
||||
for (int f = 0; f < nf; f++)
|
||||
{
|
||||
if (!FaceIsTrueInterior(f)) { continue; } // skip true boundary
|
||||
|
||||
const int e1 = faces_info[f].Elem1No;
|
||||
if (e1 < 0) { continue; }
|
||||
const int a1 = elements[e1]->GetAttribute();
|
||||
|
||||
int a2 = a1;
|
||||
if (faces_info[f].Elem2No >= 0)
|
||||
{
|
||||
a2 = elements[faces_info[f].Elem2No]->GetAttribute();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shared face: element 2 is a face-neighbor element with index
|
||||
// -1-Elem2No, i.e. FlipIndexSign(Elem2No) (see Mesh::FaceInfo).
|
||||
const int nbr_el = FlipIndexSign(faces_info[f].Elem2No);
|
||||
MFEM_ASSERT(0 <= nbr_el && nbr_el < face_nbr_elements.Size(),
|
||||
"invalid face-neighbor index");
|
||||
a2 = face_nbr_elements[nbr_el]->GetAttribute();
|
||||
}
|
||||
|
||||
if (a1 != a2) { interface.Append(f); }
|
||||
}
|
||||
}
|
||||
|
||||
void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
{
|
||||
int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc;
|
||||
int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1];
|
||||
long long vc; // global vertex offset
|
||||
const int *v;
|
||||
MPI_Status status;
|
||||
Array<real_t> vert;
|
||||
Array<int> ints;
|
||||
Array<int> interface_faces;
|
||||
int interface_bdr_attr = 0;
|
||||
|
||||
if (MyRank == 0)
|
||||
{
|
||||
@@ -5030,15 +5134,20 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
os << "\ndimension\n" << Dim;
|
||||
}
|
||||
|
||||
nv = NumOfElements;
|
||||
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
||||
long long loc_ne = NumOfElements, glob_ne = 0;
|
||||
MPI_Reduce(&loc_ne, &glob_ne, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
|
||||
if (MyRank == 0)
|
||||
{
|
||||
os << "\n\nelements\n" << ne << '\n';
|
||||
MFEM_VERIFY(static_cast<int>(glob_ne) == glob_ne,
|
||||
"integer overflow detected!");
|
||||
os << "\n\nelements\n" << glob_ne << '\n';
|
||||
for (i = 0; i < NumOfElements; i++)
|
||||
{
|
||||
// processor number + 1 as attribute and geometry type
|
||||
os << 1 << ' ' << elements[i]->GetGeometryType();
|
||||
// Print attribute + geometry:
|
||||
// * if print_shared != 0, use processor number + 1 as attribute
|
||||
// * otherwise, use the real attribute
|
||||
os << (print_shared ? 1 : GetAttribute(i))
|
||||
<< ' ' << elements[i]->GetGeometryType();
|
||||
// vertices
|
||||
nv = elements[i]->GetNVertices();
|
||||
v = elements[i]->GetVertices();
|
||||
@@ -5048,19 +5157,26 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
vc = NumOfVertices;
|
||||
vc = NumOfVertices; // global offset for vertex indices
|
||||
for (p = 1; p < NRanks; p++)
|
||||
{
|
||||
MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status);
|
||||
ints.SetSize(ne);
|
||||
if (ne)
|
||||
{
|
||||
// Receive an array that contains attribute + geometry + vertices
|
||||
// for each element.
|
||||
MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status);
|
||||
}
|
||||
MFEM_VERIFY(static_cast<int>(vc + nv) == (vc + nv),
|
||||
"integer overflow detected!");
|
||||
for (i = 0; i < ne; )
|
||||
{
|
||||
// processor number + 1 as attribute and geometry type
|
||||
os << p+1 << ' ' << ints[i];
|
||||
// Print attribute + geometry:
|
||||
// * if print_shared != 0, use processor number + 1 as attribute
|
||||
// * otherwise, use the real attribute
|
||||
os << (print_shared ? p+1 : ints[i]) << ' ' << ints[i+1];
|
||||
i++;
|
||||
// vertices
|
||||
k = Geometries.GetVertices(ints[i++])->GetNPoints();
|
||||
for (j = 0; j < k; j++)
|
||||
@@ -5074,11 +5190,11 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
else
|
||||
{
|
||||
// for each element send its geometry type and its vertices
|
||||
// for each element send its attribute, geometry type and vertices
|
||||
ne = 0;
|
||||
for (i = 0; i < NumOfElements; i++)
|
||||
{
|
||||
ne += 1 + elements[i]->GetNVertices();
|
||||
ne += 2 + elements[i]->GetNVertices(); // attribute + geom + vertices
|
||||
}
|
||||
nv = NumOfVertices;
|
||||
MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm);
|
||||
@@ -5087,7 +5203,7 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
ints.SetSize(0);
|
||||
for (i = 0; i < NumOfElements; i++)
|
||||
{
|
||||
dump_element(elements[i], ints);
|
||||
dump_element_with_attr(elements[i], ints);
|
||||
}
|
||||
MFEM_ASSERT(ints.Size() == ne, "");
|
||||
if (ne)
|
||||
@@ -5096,36 +5212,57 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
}
|
||||
|
||||
// boundary + shared boundary
|
||||
// Add material interfaces as boundary elements for visualization. We build a
|
||||
// list of local faces to print as extra boundary elements. This does not
|
||||
// modify the ParMesh object, only the printed mesh.
|
||||
if (print_interfaces && Dim > 1)
|
||||
{
|
||||
FindInterface(interface_faces);
|
||||
|
||||
// Choose a boundary attribute that does not collide with existing ones.
|
||||
const int max_bdr_attr = bdr_attributes.Size() ? bdr_attributes.Max() : 0;
|
||||
// If print_shared is enabled, this attribute will be replaced:
|
||||
interface_bdr_attr = max_bdr_attr + 1;
|
||||
}
|
||||
|
||||
// boundary + (optionally) shared faces + (optionally) interface faces
|
||||
ne = NumOfBdrElements;
|
||||
if (!pncmesh)
|
||||
if (print_shared && !pncmesh)
|
||||
{
|
||||
ne += GetNSharedFaces();
|
||||
}
|
||||
else if (Dim > 1)
|
||||
if (print_shared && pncmesh && Dim > 1)
|
||||
{
|
||||
const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
|
||||
ne += list.conforming.Size() + list.masters.Size() + list.slaves.Size();
|
||||
// In addition to the number returned by GetNSharedFaces(), include the
|
||||
// the master shared faces as well.
|
||||
}
|
||||
ints.Reserve(ne * (1 + 2*(Dim-1))); // just an upper bound
|
||||
if (print_interfaces && Dim > 1)
|
||||
{
|
||||
// In 3D we print two oriented copies for each material interface face:
|
||||
ne += (Dim == 3 ? 2 : 1) * interface_faces.Size();
|
||||
}
|
||||
ints.Reserve(ne * (2 + (1 << Dim))); // just an upper bound
|
||||
ints.SetSize(0);
|
||||
|
||||
// for each boundary and shared boundary element send its geometry type
|
||||
// and its vertices
|
||||
// For each boundary, (optionally) shared face, and (optionally) interface
|
||||
// face send its attribute, geometry type, and vertices.
|
||||
ne = 0;
|
||||
for (i = j = 0; i < NumOfBdrElements; i++)
|
||||
for (i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
dump_element(boundary[i], ints); ne++;
|
||||
dump_element_with_attr(boundary[i], ints); ne++;
|
||||
}
|
||||
if (!pncmesh)
|
||||
if (print_shared && !pncmesh)
|
||||
{
|
||||
// Attribute for visualized shared faces on parallel interfaces:
|
||||
constexpr int shared_attribute = 1;
|
||||
switch (Dim)
|
||||
{
|
||||
case 1:
|
||||
for (i = 0; i < svert_lvert.Size(); i++)
|
||||
{
|
||||
ints.Append(shared_attribute);
|
||||
ints.Append(Geometry::POINT);
|
||||
ints.Append(svert_lvert[i]);
|
||||
ne++;
|
||||
@@ -5135,19 +5272,21 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
case 2:
|
||||
for (i = 0; i < shared_edges.Size(); i++)
|
||||
{
|
||||
dump_element(shared_edges[i], ints); ne++;
|
||||
dump_element_with_attr(shared_edges[i], ints); ne++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for (i = 0; i < shared_trias.Size(); i++)
|
||||
{
|
||||
ints.Append(shared_attribute);
|
||||
ints.Append(Geometry::TRIANGLE);
|
||||
ints.Append(shared_trias[i].v, 3);
|
||||
ne++;
|
||||
}
|
||||
for (i = 0; i < shared_quads.Size(); i++)
|
||||
{
|
||||
ints.Append(shared_attribute);
|
||||
ints.Append(Geometry::SQUARE);
|
||||
ints.Append(shared_quads[i].v, 4);
|
||||
ne++;
|
||||
@@ -5158,32 +5297,85 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
MFEM_ABORT("invalid dimension: " << Dim);
|
||||
}
|
||||
}
|
||||
else if (Dim > 1)
|
||||
if (print_shared && pncmesh && Dim > 1)
|
||||
{
|
||||
const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
|
||||
const int nfaces = GetNumFaces();
|
||||
for (i = 0; i < list.conforming.Size(); i++)
|
||||
{
|
||||
int index = list.conforming[i].index;
|
||||
if (index < nfaces) { dump_element(faces[index], ints); ne++; }
|
||||
if (index < nfaces)
|
||||
{
|
||||
dump_element_with_attr(faces[index], ints); ne++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < list.masters.Size(); i++)
|
||||
{
|
||||
int index = list.masters[i].index;
|
||||
if (index < nfaces) { dump_element(faces[index], ints); ne++; }
|
||||
if (index < nfaces)
|
||||
{
|
||||
dump_element_with_attr(faces[index], ints); ne++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < list.slaves.Size(); i++)
|
||||
{
|
||||
int index = list.slaves[i].index;
|
||||
if (index < nfaces) { dump_element(faces[index], ints); ne++; }
|
||||
if (index < nfaces)
|
||||
{
|
||||
dump_element_with_attr(faces[index], ints); ne++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (print_interfaces && Dim > 1)
|
||||
{
|
||||
for (i = 0; i < interface_faces.Size(); i++)
|
||||
{
|
||||
const int f = interface_faces[i];
|
||||
const int *fv = faces[f]->GetVertices();
|
||||
if (Dim == 2)
|
||||
{
|
||||
Segment seg(fv, interface_bdr_attr);
|
||||
dump_element_with_attr(&seg, ints); ne++;
|
||||
}
|
||||
else // Dim == 3
|
||||
{
|
||||
const Geometry::Type geom = faces[f]->GetGeometryType();
|
||||
if (geom == Geometry::TRIANGLE)
|
||||
{
|
||||
int v0[3] = { fv[0], fv[1], fv[2] };
|
||||
int v1[3] = { fv[0], fv[2], fv[1] };
|
||||
Triangle t0(v0, interface_bdr_attr);
|
||||
Triangle t1(v1, interface_bdr_attr);
|
||||
dump_element_with_attr(&t0, ints); ne++;
|
||||
dump_element_with_attr(&t1, ints); ne++;
|
||||
}
|
||||
else if (geom == Geometry::SQUARE)
|
||||
{
|
||||
int v0[4] = { fv[0], fv[1], fv[2], fv[3] };
|
||||
int v1[4] = { fv[0], fv[3], fv[2], fv[1] };
|
||||
Quadrilateral q0(v0, interface_bdr_attr);
|
||||
Quadrilateral q1(v1, interface_bdr_attr);
|
||||
dump_element_with_attr(&q0, ints); ne++;
|
||||
dump_element_with_attr(&q1, ints); ne++;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("unsupported 3D face geometry type '"
|
||||
<< Geometry::Name[geom]
|
||||
<< "' while printing interface boundaries.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MPI_Reduce(&ne, &k, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
||||
long long loc_nb = ne, glob_nb = 0;
|
||||
MPI_Reduce(&loc_nb, &glob_nb, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
|
||||
if (MyRank == 0)
|
||||
{
|
||||
os << "\nboundary\n" << k << '\n';
|
||||
vc = 0;
|
||||
MFEM_VERIFY(static_cast<int>(glob_nb) == glob_nb,
|
||||
"integer overflow detected!");
|
||||
os << "\nboundary\n" << glob_nb << '\n';
|
||||
vc = 0; // global vertex offset
|
||||
for (p = 0; p < NRanks; p++)
|
||||
{
|
||||
if (p)
|
||||
@@ -5202,8 +5394,11 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
for (i = 0; i < ne; )
|
||||
{
|
||||
// processor number + 1 as bdr. attr. and bdr. geometry type
|
||||
os << p+1 << ' ' << ints[i];
|
||||
// Print bdr attribute + bdr geometry:
|
||||
// * if print_shared != 0, use processor number + 1 as bdr attribute
|
||||
// * otherwise, use the real bdr attribute
|
||||
os << (print_shared ? p+1 : ints[i]) << ' ' << ints[i+1];
|
||||
i++;
|
||||
k = Geometries.NumVerts[ints[i++]];
|
||||
// vertices
|
||||
for (j = 0; j < k; j++)
|
||||
@@ -5212,7 +5407,7 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
vc += nv;
|
||||
vc += nv; // checked for overflow above, when printing the elements
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5227,6 +5422,7 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
|
||||
// vertices / nodes
|
||||
// checked for overflow above, when printing the elements:
|
||||
MPI_Reduce(const_cast<int*>(&NumOfVertices), &nv, 1, MPI_INT, MPI_SUM, 0,
|
||||
MyComm);
|
||||
if (MyRank == 0)
|
||||
@@ -5253,8 +5449,8 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
vert.SetSize(nv*spaceDim);
|
||||
if (nv)
|
||||
{
|
||||
MPI_Recv(&vert[0], nv*spaceDim, MPITypeMap<real_t>::mpi_type, p, 449, MyComm,
|
||||
&status);
|
||||
MPI_Recv(&vert[0], nv*spaceDim, MPITypeMap<real_t>::mpi_type, p,
|
||||
449, MyComm, &status);
|
||||
}
|
||||
for (i = 0; i < nv; i++)
|
||||
{
|
||||
@@ -5281,8 +5477,8 @@ void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
|
||||
}
|
||||
if (NumOfVertices)
|
||||
{
|
||||
MPI_Send(&vert[0], NumOfVertices*spaceDim, MPITypeMap<real_t>::mpi_type, 0, 449,
|
||||
MyComm);
|
||||
MPI_Send(&vert[0], NumOfVertices*spaceDim,
|
||||
MPITypeMap<real_t>::mpi_type, 0, 449, MyComm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6879,7 +7075,9 @@ void ParMesh::Swap(ParMesh &other)
|
||||
// Nodes, NCMesh, and NURBSExtension are taken care of by Mesh::Swap
|
||||
mfem::Swap(pncmesh, other.pncmesh);
|
||||
|
||||
print_shared = other.print_shared;
|
||||
// Keep Print() behavior consistent after move/swap operations.
|
||||
mfem::Swap(print_shared, other.print_shared);
|
||||
mfem::Swap(print_interfaces, other.print_interfaces);
|
||||
}
|
||||
|
||||
void ParMesh::Destroy()
|
||||
|
||||
+45
-12
@@ -96,10 +96,12 @@ protected:
|
||||
mutable long glob_offset_sequence;
|
||||
void ComputeGlobalElementOffset() const;
|
||||
|
||||
// Enable Print() to add the parallel interface as boundary (typically used
|
||||
// for visualization purposes)
|
||||
// See SetPrintShared()
|
||||
bool print_shared = true;
|
||||
|
||||
// See SetPrintInterfaces()
|
||||
bool print_interfaces = false;
|
||||
|
||||
/// Create from a nonconforming mesh.
|
||||
ParMesh(const ParNCMesh &pncmesh);
|
||||
|
||||
@@ -303,6 +305,9 @@ protected:
|
||||
void GetSharedTriCommunicator(int ordering,
|
||||
GroupCommunicator& stria_comm) const;
|
||||
|
||||
// Optionally called by Print() and PrintAsOne() (so this needs to be const)
|
||||
void FindInterface(Array<int> &interface) const;
|
||||
|
||||
// Similar to Mesh::GetFacesTable()
|
||||
STable3D *GetSharedFacesTable();
|
||||
|
||||
@@ -720,13 +725,35 @@ public:
|
||||
begin with '#'. */
|
||||
void ParPrint(std::ostream &out, const std::string &comments = "") const;
|
||||
|
||||
// Enable Print() to add the parallel interface as boundary (typically used
|
||||
// for visualization purposes)
|
||||
/** @brief Enable Print() and PrintAsOne() to add the parallel interface as
|
||||
boundary (typically used for visualization purposes).
|
||||
|
||||
In PrintAsOne(), this setting also controls what element and boundary
|
||||
attributes are printed:
|
||||
- if @a print == false, use the real element and boundary attributes,
|
||||
- otherwise, processor rank + 1 is used for both, the element and
|
||||
boundary attributes.
|
||||
|
||||
The ParMesh object itself is not modified, this only affects file output
|
||||
for visualization.
|
||||
|
||||
The default value of this flag is true. */
|
||||
void SetPrintShared(bool print) { print_shared = print; }
|
||||
|
||||
/** @brief Enable Print() and PrintAsOne() to add material interfaces, i.e.
|
||||
intefaces between different mesh element attributes, as boundary
|
||||
(typically used for visualization purposes).
|
||||
|
||||
The ParMesh object itself is not modified, this only affects file output
|
||||
for visualization.
|
||||
|
||||
The default value of this flag is false. */
|
||||
void SetPrintInterfaces(bool print) { print_interfaces = print; }
|
||||
|
||||
/** Print the part of the mesh in the calling processor using the mfem v1.0
|
||||
format. Depending on SetPrintShared(), the parallel interface can be
|
||||
added as boundary for visualization (true by default). If @a comments is
|
||||
format. Depending on SetPrintShared() and SetPrintInterfaces(), the
|
||||
parallel interface and/or material interfaces can be added as boundary
|
||||
for visualization (true/false respectively by default). If @a comments is
|
||||
non-empty, it will be printed after the first line of the file, and each
|
||||
line should begin with '#'. */
|
||||
void Print(std::ostream &out = mfem::out,
|
||||
@@ -748,12 +775,18 @@ public:
|
||||
as boundary (for visualization purposes) using Netgen/Truegrid format .*/
|
||||
void PrintXG(std::ostream &out = mfem::out) const override;
|
||||
|
||||
/** Write the mesh to the stream 'out' on Process 0 in a form suitable for
|
||||
visualization: the mesh is written as a disjoint mesh and the shared
|
||||
boundary is added to the actual boundary; both the element and boundary
|
||||
attributes are set to the processor number. If @a comments is non-empty,
|
||||
it will be printed after the first line of the file, and each line should
|
||||
begin with '#'. */
|
||||
/** @brief Write the mesh to the stream 'out' on Process 0 in a form suitable
|
||||
for visualization.
|
||||
|
||||
The mesh is written as a disjoint mesh. If SetPrintShared() is enabled,
|
||||
the shared boundary is added to the actual boundary and both the element
|
||||
and boundary attributes are set to the processor number + 1.
|
||||
|
||||
If SetPrintInterfaces() is enabled, material interfaces, i.e. interfaces
|
||||
between different mesh element attributes, are added as boundary as well.
|
||||
|
||||
If @a comments is non-empty, it will be printed after the first line of
|
||||
the file, and each line should begin with '#'. */
|
||||
void PrintAsOne(std::ostream &out = mfem::out,
|
||||
const std::string &comments = "") const;
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ point Tribol to them. The steps are as follows:
|
||||
AXOM_DIR = @MFEM_DIR@/../axom-repo/axom
|
||||
TRIBOL_DIR = @MFEM_DIR@/../tribol-repo/tribol
|
||||
TRIBOL_OPT = -I$(TRIBOL_DIR)/include -I$(AXOM_DIR)/include
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib \
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -ltribol_shared -lredecomp -L$(AXOM_DIR)/lib \
|
||||
-laxom_mint -laxom_slam -laxom_slic -laxom_core
|
||||
```
|
||||
3. [**Axom:**](https://github.com/LLNL/axom.git) Starting from the MFEM root
|
||||
|
||||
Reference in New Issue
Block a user