Compare commits
75
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59258a8fe8 | ||
|
|
8b0a361f26 | ||
|
|
1c42f029e9 | ||
|
|
ae66dd0a90 | ||
|
|
39b8a9a569 | ||
|
|
c44a2fbf6e | ||
|
|
fe81343870 | ||
|
|
621b1a0488 | ||
|
|
5be3943da5 | ||
|
|
f6e527ba61 | ||
|
|
b1d1bd3e9e | ||
|
|
ed28db4640 | ||
|
|
a8d02a2a46 | ||
|
|
4b0e73a415 | ||
|
|
3ccf411535 | ||
|
|
bf290becfa | ||
|
|
9701881916 | ||
|
|
b98627f12f | ||
|
|
71552774c9 | ||
|
|
af0ccd56b4 | ||
|
|
9168cee577 | ||
|
|
bc6477870f | ||
|
|
d34b8e8527 | ||
|
|
b5f0193290 | ||
|
|
f08049d0e9 | ||
|
|
4e8a75fb97 | ||
|
|
3f95ae16fc | ||
|
|
e8bde768ad | ||
|
|
8eb49d7f67 | ||
|
|
8dbd8bdac3 | ||
|
|
f04a772077 | ||
|
|
f6b69d5447 | ||
|
|
9ed8ba4af9 | ||
|
|
5caee4743e | ||
|
|
c1f8ad1a08 | ||
|
|
f1019424d3 | ||
|
|
d8a74ba799 | ||
|
|
5f9800ddd3 | ||
|
|
fb5613147c | ||
|
|
afe006555d | ||
|
|
301aa746f2 | ||
|
|
003eb6943b | ||
|
|
b5fd867172 | ||
|
|
97d941bcf3 | ||
|
|
80e516f0c7 | ||
|
|
28f9c7bad7 | ||
|
|
bb779a9122 | ||
|
|
a89eabf00a | ||
|
|
753721a9e8 | ||
|
|
7832d89904 | ||
|
|
4312b33efa | ||
|
|
bb2226d0dd | ||
|
|
71046bb1c3 | ||
|
|
8b34deecb3 | ||
|
|
e646bdf338 | ||
|
|
1bc516f0b7 | ||
|
|
2faa2e0e46 | ||
|
|
2c0c9901a8 | ||
|
|
58d4012814 | ||
|
|
d6b413690b | ||
|
|
cf2a2cd435 | ||
|
|
0dea17f706 | ||
|
|
9b3873c9b8 | ||
|
|
ac52a6ed09 | ||
|
|
2ff29d266f | ||
|
|
e442843beb | ||
|
|
36d8a39cc1 | ||
|
|
bcc1021d89 | ||
|
|
91d36a3902 | ||
|
|
6e8bd2f7e6 | ||
|
|
8f4fc4be54 | ||
|
|
a51d6277ae | ||
|
|
c3fabe90e9 | ||
|
|
2f46cd3b8e | ||
|
|
814c60fa8a |
@@ -0,0 +1,61 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 30
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale
|
||||
# label is closed. Set to false to disable. If disabled, issues still need to
|
||||
# be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale.
|
||||
# Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set
|
||||
# to `[]` to disable
|
||||
exemptLabels:
|
||||
- bug
|
||||
- WIP
|
||||
- ready-for-review
|
||||
- in-review
|
||||
- in-next
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
:warning: This issue or PR has been automatically marked as stale because it has not
|
||||
had any activity in the last month. *If no activity occurs in the next week, it will
|
||||
be automatically closed.* Thank you for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
@@ -1,31 +0,0 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
# For more information, see: https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
actions: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: ':warning: This issue has been automatically marked as stale because it has not had any activity in the last month. *If no activity occurs in the next week, it will be automatically closed.* Thank you for your contributions.'
|
||||
stale-pr-message: ':warning: This PR has been automatically marked as stale because it has not had any activity in the last month. *If no activity occurs in the next week, it will be automatically closed.* Thank you for your contributions.'
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
operations-per-run: 500
|
||||
exempt-issue-labels: "bug,WIP,ready-for-review,in-review,in-next"
|
||||
exempt-pr-labels: "bug,WIP,ready-for-review,in-review,in-next"
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
Version 4.7.1 (development)
|
||||
===========================
|
||||
- Refactored ALGOIM cut integration rules. The interface is unified with
|
||||
the interface for moment based cut integration rules.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
@@ -20,8 +18,6 @@ Discretization improvements
|
||||
|
||||
- Added support for boundary constraints to the hybridization class.
|
||||
|
||||
- Added support for external boundary submeshes with nonconformal mesh adaptation.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
|
||||
@@ -54,15 +50,8 @@ GPU computing
|
||||
or by explicitly calling `KernelReporter::Enable`. Users can then add
|
||||
specializations for these kernels to achieve higher performance.
|
||||
|
||||
- Element assembly kernels have been added for low-order refined to
|
||||
high-order transfer operators. New kernels can be offloaded as device
|
||||
kernels. Example usage may be found in lor-transfer.cpp under miniapps/tools.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added support for SUNDIALS v7. See the section "API changes" for some small
|
||||
changes related to this new version.
|
||||
|
||||
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
|
||||
`TimeDependentOperator::Mult` only when the associated ODE operator is
|
||||
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
|
||||
@@ -79,18 +68,6 @@ API changes
|
||||
-----------
|
||||
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
|
||||
|
||||
- API change: support for SUNDIALS v7:
|
||||
* the SUNDIALS types `realtype` and `booleantype` are no longer defined by v7
|
||||
and therefore MFEM now uses the new type names `sunrealtype` and
|
||||
`sunbooleantype`, respectively, which MFEM defines when using SUNDIALS < v6
|
||||
where these types were not defined.
|
||||
* The SUNDIALS macro `SUNLS_SUCCESS` and some other `*_SUCCESS` macros were
|
||||
removed and replaced by `SUN_SUCCESS` in v7, so to avoid tedious checks for
|
||||
SUNDIALS versions, MFEM now defines and uses the constant `SUN_SUCCESS` when
|
||||
using SUNDIALS < v7.
|
||||
* The constants `SUN_PREC_*`, introduced by SUNDIALS v6 are now introduced by
|
||||
MFEM when using SUNDIALS < v6 to avoid tedious version checks.
|
||||
|
||||
|
||||
Version 4.7, released on May 7, 2024
|
||||
====================================
|
||||
@@ -177,15 +154,6 @@ New and updated examples and miniapps
|
||||
- Added two new example codes: 38 and 39/39p described above. Substantially
|
||||
updated Example 18/18p.
|
||||
|
||||
- Added ODE solvers selection routines. This creates a uniformity across examples,
|
||||
miniapps and other executables in regard to ODE(time-integrator) selection.
|
||||
|
||||
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
|
||||
This is relevant for AB/AM and gen-alpha solvers.
|
||||
|
||||
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
|
||||
read/write functionality.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Updated the Doxygen documentation style, which now requires Doxygen version
|
||||
|
||||
+1
-4
@@ -340,10 +340,7 @@ if (MFEM_USE_SUNDIALS)
|
||||
if (MFEM_USE_HIP)
|
||||
list(APPEND SUNDIALS_COMPONENTS NVector_Hip)
|
||||
endif()
|
||||
# The Core component was added in SUNDIALS v7, so we treat it as optional in
|
||||
# order to support older versions.
|
||||
find_package(SUNDIALS REQUIRED ${SUNDIALS_COMPONENTS}
|
||||
OPTIONAL_COMPONENTS Core)
|
||||
find_package(SUNDIALS REQUIRED ${SUNDIALS_COMPONENTS})
|
||||
endif()
|
||||
|
||||
# SuperLU_DIST can only be enabled in parallel
|
||||
|
||||
@@ -502,14 +502,10 @@ MFEM_USE_CODIPACK = YES/NO
|
||||
MFEM_USE_ALGOIM = YES/NO
|
||||
Enable the usage of Algoim - a collection of high-order accurate numerical
|
||||
methods and C++ algorithms for working with implicitly-defined geometry and
|
||||
level set methods, see https://algoim.github.io. MFEM provides interface to
|
||||
Algoim v1. To check out the specific Algoim state use:
|
||||
https://github.com/algoim/algoim
|
||||
level set methods. The Algoim library requires the Blitz++ library. The MFEM
|
||||
provides interface to Algoim v1. Thus, to check out the specific state use:
|
||||
git checkout 9c9ca0ef094d8ab0390ed36367a1151b459bbe0a
|
||||
The Algoim library requires the Blitz++ library. To use the latest state of
|
||||
Blitz++ that has been tested with MFEM, use:
|
||||
https://github.com/blitzpp/blitz
|
||||
git checkout f24a250a43dff88c31ad92916da828b7ea9a98b7
|
||||
https://algoim.github.io
|
||||
|
||||
MFEM_USE_ADFORWARD = YES/NO
|
||||
Enable forward mode for AD packages. This option is valid
|
||||
|
||||
@@ -31,5 +31,4 @@ mfem_find_package(SUNDIALS SUNDIALS SUNDIALS_DIR
|
||||
ADD_COMPONENT CVODE "include" cvode/cvode.h "lib" sundials_cvode
|
||||
ADD_COMPONENT CVODES "include" cvodes/cvodes.h "lib" sundials_cvodes
|
||||
ADD_COMPONENT ARKODE "include" arkode/arkode.h "lib" sundials_arkode
|
||||
ADD_COMPONENT KINSOL "include" kinsol/kinsol.h "lib" sundials_kinsol
|
||||
ADD_COMPONENT Core "include" sundials/sundials_core.h "lib" sundials_core)
|
||||
ADD_COMPONENT KINSOL "include" kinsol/kinsol.h "lib" sundials_kinsol)
|
||||
|
||||
+1
-10
@@ -289,13 +289,6 @@ endif
|
||||
ifeq ($(MFEM_USE_HIP),YES)
|
||||
SUNDIALS_LIB += -lsundials_nvechip
|
||||
endif
|
||||
SUNDIALS_CORE_PAT = $(subst\
|
||||
@MFEM_DIR@,$(MFEM_DIR),$(SUNDIALS_DIR))/lib*/libsundials_core.*
|
||||
ifeq ($(MFEM_USE_SUNDIALS),YES)
|
||||
ifneq ($(wildcard $(SUNDIALS_CORE_PAT)),)
|
||||
SUNDIALS_LIB += -lsundials_core
|
||||
endif
|
||||
endif
|
||||
# If SUNDIALS was built with KLU:
|
||||
# MFEM_USE_SUITESPARSE = YES
|
||||
|
||||
@@ -540,10 +533,8 @@ ifdef GOTCHA_DIR
|
||||
endif
|
||||
|
||||
# BLITZ library configuration
|
||||
# BLITZ_DIR must be the custom installation folder (-DCMAKE_INSTALL_PREFIX).
|
||||
BLITZ_DIR = @MFEM_DIR@/../blitz/install
|
||||
BLITZ_DIR = @MFEM_DIR@/../blitz
|
||||
BLITZ_OPT = -I$(BLITZ_DIR)/include
|
||||
# On intel machines, use /lib64 instead of /lib.
|
||||
BLITZ_LIB = $(XLINKER)-rpath,$(BLITZ_DIR)/lib -L$(BLITZ_DIR)/lib -lblitz
|
||||
|
||||
# ALGOIM library configuration
|
||||
|
||||
+37
-11
@@ -3,14 +3,14 @@
|
||||
// Compile with: make ex10
|
||||
//
|
||||
// Sample runs:
|
||||
// ex10 -m ../data/beam-quad.mesh -s 23 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tri.mesh -s 23 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-hex.mesh -s 22 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tet.mesh -s 22 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-wedge.mesh -s 22 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-quad.mesh -s 4 -r 2 -o 2 -dt 0.03 -vs 20
|
||||
// ex10 -m ../data/beam-hex.mesh -s 4 -r 1 -o 2 -dt 0.05 -vs 20
|
||||
// ex10 -m ../data/beam-quad-amr.mesh -s 23 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-quad.mesh -s 3 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tri.mesh -s 3 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-hex.mesh -s 2 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tet.mesh -s 2 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-wedge.mesh -s 2 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-quad.mesh -s 14 -r 2 -o 2 -dt 0.03 -vs 20
|
||||
// ex10 -m ../data/beam-hex.mesh -s 14 -r 1 -o 2 -dt 0.05 -vs 20
|
||||
// ex10 -m ../data/beam-quad-amr.mesh -s 3 -r 2 -o 2 -dt 3
|
||||
//
|
||||
// Description: This examples solves a time dependent nonlinear elasticity
|
||||
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
|
||||
@@ -160,7 +160,7 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/beam-quad.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 2;
|
||||
int ode_solver_type = 23;
|
||||
int ode_solver_type = 3;
|
||||
real_t t_final = 300.0;
|
||||
real_t dt = 3.0;
|
||||
real_t visc = 1e-2;
|
||||
@@ -177,7 +177,11 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
" 11 - Forward Euler, 12 - RK2,\n\t"
|
||||
" 13 - RK3 SSP, 14 - RK4."
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -209,7 +213,28 @@ int main(int argc, char *argv[])
|
||||
// 3. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
@@ -346,6 +371,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+40
-11
@@ -3,14 +3,14 @@
|
||||
// Compile with: make ex10p
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 23 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tri.mesh -s 23 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 22 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tet.mesh -s 22 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-wedge.mesh -s 22 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 4 -rs 2 -dt 0.03 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 4 -rs 1 -dt 0.05 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad-amr.mesh -s 23 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 3 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tri.mesh -s 3 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 2 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tet.mesh -s 2 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-wedge.mesh -s 2 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 14 -rs 2 -dt 0.03 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 14 -rs 1 -dt 0.05 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad-amr.mesh -s 3 -rs 2 -dt 3
|
||||
//
|
||||
// Description: This examples solves a time dependent nonlinear elasticity
|
||||
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
|
||||
@@ -172,7 +172,7 @@ int main(int argc, char *argv[])
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 0;
|
||||
int order = 2;
|
||||
int ode_solver_type = 23;
|
||||
int ode_solver_type = 3;
|
||||
real_t t_final = 300.0;
|
||||
real_t dt = 3.0;
|
||||
real_t visc = 1e-2;
|
||||
@@ -192,7 +192,11 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
" 11 - Forward Euler, 12 - RK2,\n\t"
|
||||
" 13 - RK3 SSP, 14 - RK4."
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -234,7 +238,31 @@ int main(int argc, char *argv[])
|
||||
// 4. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
}
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
@@ -405,6 +433,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 12. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+30
-9
@@ -5,10 +5,10 @@
|
||||
// Sample runs: ex16
|
||||
// ex16 -m ../data/inline-tri.mesh
|
||||
// ex16 -m ../data/disc-nurbs.mesh -tf 2
|
||||
// ex16 -s 21 -a 0.0 -k 1.0
|
||||
// ex16 -s 22 -a 1.0 -k 0.0
|
||||
// ex16 -s 23 -a 0.5 -k 0.5 -o 4
|
||||
// ex16 -s 4 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// ex16 -s 1 -a 0.0 -k 1.0
|
||||
// ex16 -s 2 -a 1.0 -k 0.0
|
||||
// ex16 -s 3 -a 0.5 -k 0.5 -o 4
|
||||
// ex16 -s 14 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// ex16 -m ../data/fichera-q2.mesh
|
||||
// ex16 -m ../data/fichera-mixed.mesh
|
||||
// ex16 -m ../data/escher.mesh
|
||||
@@ -95,13 +95,11 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 2;
|
||||
|
||||
int ode_solver_type = 23; // SDIRK33Solver
|
||||
int ode_solver_type = 3;
|
||||
real_t t_final = 0.5;
|
||||
real_t dt = 1.0e-2;
|
||||
real_t alpha = 1.0e-2;
|
||||
real_t kappa = 0.5;
|
||||
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
@@ -117,7 +115,8 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
"\t 11 - Forward Euler, 12 - RK2, 13 - RK3 SSP, 14 - RK4.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -150,7 +149,28 @@ int main(int argc, char *argv[])
|
||||
// 3. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
@@ -267,6 +287,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+30
-9
@@ -5,10 +5,10 @@
|
||||
// Sample runs: mpirun -np 4 ex16p
|
||||
// mpirun -np 4 ex16p -m ../data/inline-tri.mesh
|
||||
// mpirun -np 4 ex16p -m ../data/disc-nurbs.mesh -tf 2
|
||||
// mpirun -np 4 ex16p -s 21 -a 0.0 -k 1.0
|
||||
// mpirun -np 4 ex16p -s 22 -a 1.0 -k 0.0
|
||||
// mpirun -np 8 ex16p -s 23 -a 0.5 -k 0.5 -o 4
|
||||
// mpirun -np 4 ex16p -s 4 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// mpirun -np 4 ex16p -s 1 -a 0.0 -k 1.0
|
||||
// mpirun -np 4 ex16p -s 2 -a 1.0 -k 0.0
|
||||
// mpirun -np 8 ex16p -s 3 -a 0.5 -k 0.5 -o 4
|
||||
// mpirun -np 4 ex16p -s 14 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// mpirun -np 16 ex16p -m ../data/fichera-q2.mesh
|
||||
// mpirun -np 16 ex16p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 16 ex16p -m ../data/escher-p2.mesh
|
||||
@@ -104,13 +104,11 @@ int main(int argc, char *argv[])
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 1;
|
||||
int order = 2;
|
||||
|
||||
int ode_solver_type = 23; // SDIRK33Solver
|
||||
int ode_solver_type = 3;
|
||||
real_t t_final = 0.5;
|
||||
real_t dt = 1.0e-2;
|
||||
real_t alpha = 1.0e-2;
|
||||
real_t kappa = 0.5;
|
||||
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
@@ -129,7 +127,8 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
"\t 11 - Forward Euler, 12 - RK2, 13 - RK3 SSP, 14 - RK4.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -170,7 +169,28 @@ int main(int argc, char *argv[])
|
||||
// 4. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
@@ -356,6 +376,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 12. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+17
-2
@@ -90,7 +90,8 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::ExplicitTypes.c_str());
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step. Positive number skips CFL timestep calculation.");
|
||||
@@ -124,7 +125,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectExplicit(ode_solver_type);
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
@@ -292,5 +304,8 @@ int main(int argc, char *argv[])
|
||||
const real_t error = sol.ComputeLpError(2, u0);
|
||||
cout << "Solution error: " << error << endl;
|
||||
|
||||
// Free the used memory.
|
||||
delete ode_solver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+17
-2
@@ -99,7 +99,8 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::ExplicitTypes.c_str());
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step. Positive number skips CFL timestep calculation.");
|
||||
@@ -147,7 +148,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectExplicit(ode_solver_type);
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
@@ -348,5 +360,8 @@ int main(int argc, char *argv[])
|
||||
cout << "Solution error: " << error << endl;
|
||||
}
|
||||
|
||||
// Free the used memory.
|
||||
delete ode_solver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+29
-2
@@ -201,7 +201,9 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
SecondOrderODESolver::Types.c_str());
|
||||
"ODE solver: [0--10] - GeneralizedAlpha(0.1 * s),\n\t"
|
||||
"\t 11 - Average Acceleration, 12 - Linear Acceleration\n"
|
||||
"\t 13 - CentralDifference, 14 - FoxGoodwin");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -236,7 +238,32 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several second order
|
||||
// time integrators are available.
|
||||
SecondOrderODESolver *ode_solver= SecondOrderODESolver::Select(ode_solver_type);
|
||||
SecondOrderODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit methods
|
||||
case 0: ode_solver = new GeneralizedAlpha2Solver(0.0); break;
|
||||
case 1: ode_solver = new GeneralizedAlpha2Solver(0.1); break;
|
||||
case 2: ode_solver = new GeneralizedAlpha2Solver(0.2); break;
|
||||
case 3: ode_solver = new GeneralizedAlpha2Solver(0.3); break;
|
||||
case 4: ode_solver = new GeneralizedAlpha2Solver(0.4); break;
|
||||
case 5: ode_solver = new GeneralizedAlpha2Solver(0.5); break;
|
||||
case 6: ode_solver = new GeneralizedAlpha2Solver(0.6); break;
|
||||
case 7: ode_solver = new GeneralizedAlpha2Solver(0.7); break;
|
||||
case 8: ode_solver = new GeneralizedAlpha2Solver(0.8); break;
|
||||
case 9: ode_solver = new GeneralizedAlpha2Solver(0.9); break;
|
||||
case 10: ode_solver = new GeneralizedAlpha2Solver(1.0); break;
|
||||
|
||||
case 11: ode_solver = new AverageAccelerationSolver(); break;
|
||||
case 12: ode_solver = new LinearAccelerationSolver(); break;
|
||||
case 13: ode_solver = new CentralDifferenceSolver(); break;
|
||||
case 14: ode_solver = new FoxGoodwinSolver(); break;
|
||||
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
|
||||
+84
-118
@@ -3,18 +3,18 @@
|
||||
// Compile with: make ex38
|
||||
//
|
||||
// Sample runs:
|
||||
// (since all sample runs require LAPACK or ALGOIM, the * symbol is used to
|
||||
// exclude them from the automatically generated internal MFEM tests).
|
||||
// (since all sample runs require LAPACK, the * symbol is used to exclude them
|
||||
// from the automatically generated internal MFEM tests).
|
||||
// * ex38
|
||||
// * ex38 -i volumetric1d
|
||||
// * ex38 -i surface2d
|
||||
// * ex38 -i surface2d -o 4 -r 5 -m 1
|
||||
// * ex38 -i surface2d -o 4 -r 5
|
||||
// * ex38 -i volumetric2d
|
||||
// * ex38 -i volumetric2d -o 4 -r 5 -m 1
|
||||
// * ex38 -i volumetric2d -o 4 -r 5
|
||||
// * ex38 -i surface3d
|
||||
// * ex38 -i surface3d -o 3 -r 4 -m 1
|
||||
// * ex38 -i surface3d -o 4 -r 5
|
||||
// * ex38 -i volumetric3d
|
||||
// * ex38 -i volumetric3d -o 3 -r 4 -m 1
|
||||
// * ex38 -i volumetric3d -o 4 -r 5
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to integrate
|
||||
// functions over implicit interfaces and subdomains bounded by
|
||||
@@ -71,7 +71,7 @@ real_t integrand(const Vector& X)
|
||||
switch (itype)
|
||||
{
|
||||
case IntegrationType::Volumetric1D:
|
||||
return pow(X(0), 2.);
|
||||
return 1.;
|
||||
case IntegrationType::Surface2D:
|
||||
return 3. * pow(X(0), 2.) - pow(X(1), 2.);
|
||||
case IntegrationType::Volumetric2D:
|
||||
@@ -91,7 +91,7 @@ real_t Surface()
|
||||
switch (itype)
|
||||
{
|
||||
case IntegrationType::Volumetric1D:
|
||||
return .3025;
|
||||
return 1.;
|
||||
case IntegrationType::Surface2D:
|
||||
return 2. * M_PI;
|
||||
case IntegrationType::Volumetric2D:
|
||||
@@ -111,7 +111,7 @@ real_t Volume()
|
||||
switch (itype)
|
||||
{
|
||||
case IntegrationType::Volumetric1D:
|
||||
return pow(.55, 3.) / 3.;
|
||||
return .55;
|
||||
case IntegrationType::Surface2D:
|
||||
return NAN;
|
||||
case IntegrationType::Volumetric2D:
|
||||
@@ -125,6 +125,7 @@ real_t Volume()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
/**
|
||||
@brief Class for surface IntegrationRule
|
||||
|
||||
@@ -134,14 +135,11 @@ real_t Volume()
|
||||
class SIntegrationRule : public IntegrationRule
|
||||
{
|
||||
protected:
|
||||
/// method 0 is moments-based, 1 is Algoim.
|
||||
int method, ir_order, ls_order;
|
||||
Coefficient &level_set;
|
||||
/// Space Dimension of the IntegrationRule
|
||||
/// @brief Space Dimension of the IntegrationRule
|
||||
int dim;
|
||||
/// Column-wise matrix of the quadtrature weights
|
||||
/// @brief Column-wise matrix of the quadtrature weights
|
||||
DenseMatrix Weights;
|
||||
/// Column-wise matrix of the transformation weights of the normal
|
||||
/// @brief Column-wise matrix of the transformation weights of the normal
|
||||
DenseMatrix SurfaceWeights;
|
||||
|
||||
public:
|
||||
@@ -155,21 +153,15 @@ public:
|
||||
@param [in] lsOrder Polynomial degree for approx of level-set function
|
||||
@param [in] mesh Pointer to the mesh that is used
|
||||
*/
|
||||
SIntegrationRule(int method_, int Order,
|
||||
Coefficient& LvlSet, int lsOrder, Mesh* mesh)
|
||||
: method(method_), ir_order(Order), ls_order(lsOrder),
|
||||
level_set(LvlSet), dim(mesh->Dimension())
|
||||
SIntegrationRule(int Order, Coefficient& LvlSet, int lsOrder, Mesh* mesh)
|
||||
{
|
||||
// Nothing gets pre-computed for Algoim.
|
||||
if (method == 1) { return; }
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
MomentFittingIntRules mf_ir(ir_order, level_set, ls_order);
|
||||
dim = mesh->Dimension();
|
||||
|
||||
IsoparametricTransformation Tr;
|
||||
MomentFittingIntRules MFIRs(Order, LvlSet, lsOrder);
|
||||
mesh->GetElementTransformation(0, &Tr);
|
||||
IntegrationRule ir;
|
||||
mf_ir.GetSurfaceIntegrationRule(Tr, ir);
|
||||
MFIRs.GetSurfaceIntegrationRule(Tr, ir);
|
||||
if (dim >1)
|
||||
{
|
||||
Weights.SetSize(ir.GetNPoints(), mesh->GetNE());
|
||||
@@ -180,7 +172,7 @@ public:
|
||||
}
|
||||
SurfaceWeights.SetSize(ir.GetNPoints(), mesh->GetNE());
|
||||
Vector w;
|
||||
mf_ir.GetSurfaceWeights(Tr, ir, w);
|
||||
MFIRs.GetSurfaceWeights(Tr, ir, w);
|
||||
SurfaceWeights.SetCol(0, w);
|
||||
SetSize(ir.GetNPoints());
|
||||
|
||||
@@ -206,8 +198,8 @@ public:
|
||||
for (int elem = 1; elem < mesh->GetNE(); elem++)
|
||||
{
|
||||
mesh->GetElementTransformation(elem, &Tr);
|
||||
mf_ir.GetSurfaceIntegrationRule(Tr, ir);
|
||||
mf_ir.GetSurfaceWeights(Tr, ir, w);
|
||||
MFIRs.GetSurfaceIntegrationRule(Tr, ir);
|
||||
MFIRs.GetSurfaceWeights(Tr, ir, w);
|
||||
SurfaceWeights.SetCol(elem, w);
|
||||
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
@@ -223,48 +215,48 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
MFEM_ABORT("Moment-fitting requires MFEM to be built with LAPACK!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set the weights for the given element and multiply them with the
|
||||
transformation of the interface
|
||||
*/
|
||||
void SetElementAndSurfaceWeight(ElementTransformation &Tr)
|
||||
void SetElementinclSurfaceWeight(int Element)
|
||||
{
|
||||
if (method == 1)
|
||||
{
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
AlgoimIntegrationRules a_ir(ir_order, level_set, ls_order);
|
||||
a_ir.GetSurfaceIntegrationRule(Tr, *this);
|
||||
Vector w;
|
||||
a_ir.GetSurfaceWeights(Tr, *this, w);
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntPoint(ip).weight *= w(ip);
|
||||
}
|
||||
return;
|
||||
#else
|
||||
MFEM_ABORT("MFEM is not built with Algoim support!");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dim == 1)
|
||||
{
|
||||
IntPoint(0).x = Weights(0, Tr.ElementNo);
|
||||
IntPoint(0).weight = Weights(1, Tr.ElementNo);
|
||||
IntegrationPoint &intp = IntPoint(0);
|
||||
intp.x = Weights(0, Element);
|
||||
intp.weight = Weights(1, Element);
|
||||
cout << intp.x << " " << Element << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntPoint(ip).weight = Weights(ip, Tr.ElementNo) *
|
||||
SurfaceWeights(ip, Tr.ElementNo);
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.weight = Weights(ip, Element) * SurfaceWeights(ip, Element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Set the weights for the given element
|
||||
void SetElement(int Element)
|
||||
{
|
||||
if (dim == 1)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(0);
|
||||
intp.x = Weights(0, Element);
|
||||
intp.weight = Weights(1, Element);
|
||||
}
|
||||
else
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.weight = Weights(ip, Element);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Destructor of SIntegrationRule
|
||||
~SIntegrationRule() {}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -276,12 +268,9 @@ public:
|
||||
class CIntegrationRule : public IntegrationRule
|
||||
{
|
||||
protected:
|
||||
/// method 0 is moments-based, 1 is Algoim.
|
||||
int method, ir_order, ls_order;
|
||||
Coefficient &level_set;
|
||||
/// Space Dimension of the IntegrationRule
|
||||
/// @brief Space Dimension of the IntegrationRule
|
||||
int dim;
|
||||
/// Column-wise matrix of the quadtrature positions and weights.
|
||||
/// @brief Column-wise matrix of the quadtrature weights
|
||||
DenseMatrix Weights;
|
||||
|
||||
public:
|
||||
@@ -295,21 +284,15 @@ public:
|
||||
@param [in] lsOrder Polynomial degree for approx of level-set function
|
||||
@param [in] mesh Pointer to the mesh that is used
|
||||
*/
|
||||
CIntegrationRule(int method_, int Order,
|
||||
Coefficient &LvlSet, int lsOrder, Mesh *mesh)
|
||||
: method(method_), ir_order(Order), ls_order(lsOrder),
|
||||
level_set(LvlSet), dim(mesh->Dimension())
|
||||
CIntegrationRule(int Order, Coefficient& LvlSet, int lsOrder, Mesh* mesh)
|
||||
{
|
||||
// Nothing gets pre-computed for Algoim.
|
||||
if (method == 1) { return; }
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
MomentFittingIntRules mf_ir(ir_order, level_set, ls_order);
|
||||
dim = mesh->Dimension();
|
||||
|
||||
IsoparametricTransformation Tr;
|
||||
MomentFittingIntRules MFIRs(Order, LvlSet, lsOrder);
|
||||
mesh->GetElementTransformation(0, &Tr);
|
||||
IntegrationRule ir;
|
||||
mf_ir.GetVolumeIntegrationRule(Tr, ir);
|
||||
MFIRs.GetVolumeIntegrationRule(Tr, ir);
|
||||
if (dim > 1)
|
||||
{
|
||||
Weights.SetSize(ir.GetNPoints(), mesh->GetNE());
|
||||
@@ -341,9 +324,9 @@ public:
|
||||
for (int elem = 1; elem < mesh->GetNE(); elem++)
|
||||
{
|
||||
mesh->GetElementTransformation(elem, &Tr);
|
||||
mf_ir.GetVolumeIntegrationRule(Tr, ir);
|
||||
MFIRs.GetVolumeIntegrationRule(Tr, ir);
|
||||
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
if (dim > 1)
|
||||
{
|
||||
@@ -356,39 +339,29 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
MFEM_ABORT("Moment-fitting requires MFEM to be built with LAPACK!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Set the weights for the given element
|
||||
void SetElement(ElementTransformation &Tr)
|
||||
void SetElement(int Element)
|
||||
{
|
||||
if (method == 1)
|
||||
{
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
AlgoimIntegrationRules a_ir(ir_order, level_set, ls_order);
|
||||
a_ir.GetVolumeIntegrationRule(Tr, *this);
|
||||
return;
|
||||
#else
|
||||
MFEM_ABORT("MFEM is not built with Algoim support!");
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
if (dim == 1)
|
||||
if (dim == 1)
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
intp.x = Weights(2 * ip, Tr.ElementNo);
|
||||
intp.weight = Weights(2 * ip + 1, Tr.ElementNo);
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.x = Weights(2 * ip, Element);
|
||||
intp.weight = Weights(2 * ip + 1, Element);
|
||||
}
|
||||
else
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.weight = Weights(ip, Element);
|
||||
}
|
||||
else { intp.weight = Weights(ip, Tr.ElementNo); }
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Destructor of CIntegrationRule
|
||||
~CIntegrationRule() {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Class for surface linearform integrator
|
||||
|
||||
@@ -445,7 +418,7 @@ public:
|
||||
elvect = 0.;
|
||||
|
||||
// Update the surface integration rule for the current element
|
||||
SIntRule->SetElementAndSurfaceWeight(Tr);
|
||||
SIntRule->SetElementinclSurfaceWeight(Tr.ElementNo);
|
||||
|
||||
for (int ip = 0; ip < SIntRule->GetNPoints(); ip++)
|
||||
{
|
||||
@@ -455,8 +428,6 @@ public:
|
||||
add(elvect, SIntRule->IntPoint(ip).weight * val, shape, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -515,7 +486,7 @@ public:
|
||||
elvect = 0.;
|
||||
|
||||
// Update the subdomain integration rule
|
||||
CIntRule->SetElement(Tr);
|
||||
CIntRule->SetElement(Tr.ElementNo);
|
||||
|
||||
for (int ip = 0; ip < CIntRule->GetNPoints(); ip++)
|
||||
{
|
||||
@@ -526,17 +497,18 @@ public:
|
||||
add(elvect, CIntRule->IntPoint(ip).weight * val, shape, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
#endif // MFEM_USE_LAPACK
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if defined(MFEM_USE_LAPACK) || defined(MFEM_USE_ALGOIM)
|
||||
#ifndef MFEM_USE_LAPACK
|
||||
cout << "MFEM must be built with LAPACK for this example." << endl;
|
||||
return MFEM_SKIP_RETURN_VALUE;
|
||||
#else
|
||||
// 1. Parse he command-line options.
|
||||
int ref_levels = 3;
|
||||
int order = 2;
|
||||
int method = 0;
|
||||
const char *inttype = "surface2d";
|
||||
bool visualization = true;
|
||||
itype = IntegrationType::Surface2D;
|
||||
@@ -544,8 +516,6 @@ int main(int argc, char *argv[])
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&order, "-o", "--order", "Order of quadrature rule");
|
||||
args.AddOption(&ref_levels, "-r", "--refine", "Number of meh refinements");
|
||||
args.AddOption(&method, "-m", "--method",
|
||||
"Cut integration method: 0 for moments-based, 1 for Algoim.");
|
||||
args.AddOption(&inttype, "-i", "--integrationtype",
|
||||
"IntegrationType to demonstrate");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -580,7 +550,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 2. Construct and refine the mesh.
|
||||
Mesh *mesh = nullptr;
|
||||
Mesh *mesh;
|
||||
if (itype == IntegrationType::Volumetric1D)
|
||||
{
|
||||
mesh = new Mesh("../data/inline-segment.mesh");
|
||||
@@ -628,14 +598,13 @@ int main(int argc, char *argv[])
|
||||
// 5. Define the necessary Integration rules on element 0.
|
||||
IsoparametricTransformation Tr;
|
||||
mesh->GetElementTransformation(0, &Tr);
|
||||
SIntegrationRule* sir = new SIntegrationRule(method, order,
|
||||
levelset, 2, mesh);
|
||||
SIntegrationRule* sir = new SIntegrationRule(order, levelset, 2, mesh);
|
||||
CIntegrationRule* cir = NULL;
|
||||
if (itype == IntegrationType::Volumetric1D
|
||||
|| itype == IntegrationType::Volumetric2D
|
||||
|| itype == IntegrationType::Volumetric3D)
|
||||
{
|
||||
cir = new CIntegrationRule(method, order, levelset, 2, mesh);
|
||||
cir = new CIntegrationRule(order, levelset, 2, mesh);
|
||||
}
|
||||
|
||||
// 6. Define and assemble the linear forms on the finite element space.
|
||||
@@ -678,11 +647,11 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of div free basis functions: " << nbasis << endl;
|
||||
cout << "Number of quadrature points: " << ir.GetNPoints() << endl;
|
||||
}
|
||||
cout << scientific << setprecision(10);
|
||||
cout << scientific << setprecision(2);
|
||||
cout << "============================================" << endl;
|
||||
cout << "Computed value of surface integral: " << surface.Sum() << endl;
|
||||
cout << "True value of surface integral: " << Surface() << endl;
|
||||
cout << "Absolute Error (Surface): ";
|
||||
cout << "Absolute Error (Surface): ";
|
||||
cout << abs(surface.Sum() - Surface()) << endl;
|
||||
cout << "Relative Error (Surface): ";
|
||||
cout << abs(surface.Sum() - Surface()) / Surface() << endl;
|
||||
@@ -693,7 +662,7 @@ int main(int argc, char *argv[])
|
||||
cout << "--------------------------------------------" << endl;
|
||||
cout << "Computed value of volume integral: " << volume.Sum() << endl;
|
||||
cout << "True value of volume integral: " << Volume() << endl;
|
||||
cout << "Absolute Error (Volume): ";
|
||||
cout << "Absolute Error (Volume): ";
|
||||
cout << abs(volume.Sum() - Volume()) << endl;
|
||||
cout << "Relative Error (Volume): ";
|
||||
cout << abs(volume.Sum() - Volume()) / Volume() << endl;
|
||||
@@ -722,8 +691,5 @@ int main(int argc, char *argv[])
|
||||
delete fespace;
|
||||
delete mesh;
|
||||
return EXIT_SUCCESS;
|
||||
#else
|
||||
cout << "MFEM must be built with LAPACK or ALGOIM for this example." << endl;
|
||||
return MFEM_SKIP_RETURN_VALUE;
|
||||
#endif // MFEM_USE_LAPACK
|
||||
#endif //MFEM_USE_LAPACK
|
||||
}
|
||||
|
||||
+30
-3
@@ -9,7 +9,7 @@
|
||||
// ex9 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9
|
||||
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 23 -tf 9
|
||||
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 13 -tf 9
|
||||
// ex9 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9
|
||||
@@ -182,7 +182,12 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
||||
" 11 - Backward Euler,\n\t"
|
||||
" 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t"
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -219,7 +224,28 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit methods
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
// Implicit (L-stable) methods
|
||||
case 11: ode_solver = new BackwardEulerSolver; break;
|
||||
case 12: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 13: ode_solver = new SDIRK33Solver; break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
@@ -414,6 +440,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete pd;
|
||||
delete dc;
|
||||
|
||||
|
||||
+33
-3
@@ -9,7 +9,7 @@
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.002 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.02 -s 23 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.02 -s 13 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/star-q3.mesh -p 1 -rp 1 -dt 0.004 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/star-mixed.mesh -p 1 -rp 1 -dt 0.004 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9
|
||||
@@ -285,7 +285,12 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
||||
" 11 - Backward Euler,\n\t"
|
||||
" 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t"
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -333,7 +338,31 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 4. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit methods
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
// Implicit (L-stable) methods
|
||||
case 11: ode_solver = new BackwardEulerSolver; break;
|
||||
case 12: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 13: ode_solver = new SDIRK33Solver; break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
}
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
@@ -613,6 +642,7 @@ int main(int argc, char *argv[])
|
||||
delete m;
|
||||
delete fes;
|
||||
delete pmesh;
|
||||
delete ode_solver;
|
||||
delete pd;
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
if (adios2)
|
||||
|
||||
@@ -486,11 +486,7 @@ int main(int argc, char *argv[])
|
||||
arkode = new ARKStepSolver(ARKStepSolver::IMPLICIT);
|
||||
arkode->Init(*oper);
|
||||
arkode->SetSStolerances(reltol, abstol);
|
||||
#if MFEM_SUNDIALS_VERSION < 70100
|
||||
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#else
|
||||
ARKodeSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#endif
|
||||
arkode->SetMaxStep(dt);
|
||||
if (ode_solver_type == 15)
|
||||
{
|
||||
|
||||
@@ -541,11 +541,7 @@ int main(int argc, char *argv[])
|
||||
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::IMPLICIT);
|
||||
arkode->Init(*oper);
|
||||
arkode->SetSStolerances(reltol, abstol);
|
||||
#if MFEM_SUNDIALS_VERSION < 70100
|
||||
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#else
|
||||
ARKodeSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#endif
|
||||
arkode->SetMaxStep(dt);
|
||||
if (ode_solver_type == 15)
|
||||
{
|
||||
|
||||
@@ -447,7 +447,7 @@ ConductionOperator::ConductionOperator(FiniteElementSpace &fes,
|
||||
const Vector &u,
|
||||
const Type &ode_expression_type)
|
||||
: TimeDependentOperator(fes.GetTrueVSize(), 0.0, ode_expression_type),
|
||||
fespace(fes), M(&fespace), alpha(alpha), kappa(kappa), z(height)
|
||||
fespace(fes), alpha(alpha), kappa(kappa), M(&fespace), z(height)
|
||||
{
|
||||
// specify a relative tolerance for all solves with MFEM integrators
|
||||
const real_t rel_tol = 1e-8;
|
||||
@@ -522,7 +522,7 @@ int ConductionOperator::SUNImplicitSetup(const Vector &u, const Vector &fu,
|
||||
T = std::unique_ptr<SparseMatrix>(Add(1.0, Mmat, gam, Kmat));
|
||||
T_solver.SetOperator(*T);
|
||||
*jcur = SUNTRUE; // this should eventually only be set true if K(u) is used
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
@@ -544,7 +544,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
}
|
||||
if (T_solver.GetConverged())
|
||||
{
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -555,7 +555,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
int ConductionOperator::SUNMassSetup()
|
||||
{
|
||||
// Do nothing b/c mass solver was setup in constructor.
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
@@ -565,7 +565,7 @@ int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
M_solver.Mult(b, x);
|
||||
if (M_solver.GetConverged())
|
||||
{
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -577,6 +577,6 @@ int ConductionOperator::SUNMassMult(const Vector &x, Vector &v)
|
||||
{
|
||||
// Compute M x.
|
||||
Mmat.Mult(x, v);
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -499,7 +499,7 @@ ConductionOperator::ConductionOperator(ParFiniteElementSpace &fes,
|
||||
const Vector &u,
|
||||
const Type &ode_expression_type)
|
||||
: TimeDependentOperator(fes.GetTrueVSize(), 0.0, ode_expression_type),
|
||||
fespace(fes), M(&fespace), alpha(alpha), kappa(kappa),
|
||||
fespace(fes), alpha(alpha), kappa(kappa), M(&fespace),
|
||||
M_solver(fes.GetComm()), T_solver(fes.GetComm()), z(height)
|
||||
{
|
||||
// specify a relative tolerance for all solves with MFEM integrators
|
||||
@@ -576,7 +576,7 @@ int ConductionOperator::SUNImplicitSetup(const Vector &u, const Vector &fu,
|
||||
T = std::unique_ptr<HypreParMatrix>(Add(1.0, Mmat, gam, Kmat));
|
||||
T_solver.SetOperator(*T);
|
||||
*jcur = SUNTRUE; // this should eventually only be set true if K(u) is used
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
@@ -598,7 +598,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
}
|
||||
if (T_solver.GetConverged())
|
||||
{
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -609,7 +609,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
int ConductionOperator::SUNMassSetup()
|
||||
{
|
||||
// Do nothing b/c mass solver was setup in constructor.
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
@@ -619,7 +619,7 @@ int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
M_solver.Mult(b, x);
|
||||
if (M_solver.GetConverged())
|
||||
{
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -631,5 +631,5 @@ int ConductionOperator::SUNMassMult(const Vector &x, Vector &v)
|
||||
{
|
||||
// Compute M x.
|
||||
Mmat.Mult(x, v);
|
||||
return SUN_SUCCESS;
|
||||
return SUNLS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ set(SRCS
|
||||
lor/lor_ads.cpp
|
||||
lor/lor_ams.cpp
|
||||
lor/lor_batched.cpp
|
||||
mdgridfunc.hpp
|
||||
multigrid.cpp
|
||||
nonlinearform.cpp
|
||||
nonlinearform_ext.cpp
|
||||
|
||||
+26
-249
@@ -289,10 +289,9 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
|
||||
return;
|
||||
}
|
||||
|
||||
const FiniteElement &fe = *fes->GetFE(i);
|
||||
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
const FiniteElement &fe = *fes->GetFE(i);
|
||||
ElementTransformation *eltrans = fes->GetElementTransformation(i);
|
||||
domain_integs[0]->AssembleElementMatrix(fe, *eltrans, elmat);
|
||||
for (int k = 1; k < domain_integs.Size(); k++)
|
||||
@@ -303,18 +302,17 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
|
||||
}
|
||||
else
|
||||
{
|
||||
const int ndof = fe.GetDof() * fes->GetVDim();
|
||||
elmat.SetSize(ndof);
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
elmat.SetSize(vdofs.Size());
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
|
||||
{
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
ElementTransformation *eltrans = fes->GetBdrElementTransformation(i);
|
||||
boundary_integs[0]->AssembleElementMatrix(be, *eltrans, elmat);
|
||||
for (int k = 1; k < boundary_integs.Size(); k++)
|
||||
@@ -325,8 +323,8 @@ void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
|
||||
}
|
||||
else
|
||||
{
|
||||
const int ndof = be.GetDof() * fes->GetVDim();
|
||||
elmat.SetSize(ndof);
|
||||
fes->GetBdrElementVDofs(i, vdofs);
|
||||
elmat.SetSize(vdofs.Size());
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
@@ -1431,50 +1429,32 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
|
||||
mat->GetBlocks(blocks);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
|
||||
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
domain_integs.Append(bfi);
|
||||
domain_integs.Append (bfi);
|
||||
domain_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &elem_marker)
|
||||
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &elem_marker)
|
||||
{
|
||||
domain_integs.Append(bfi);
|
||||
domain_integs.Append (bfi);
|
||||
domain_integs_marker.Append(&elem_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi)
|
||||
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
boundary_integs.Append(bfi);
|
||||
boundary_integs.Append (bfi);
|
||||
boundary_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &bdr_marker)
|
||||
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker)
|
||||
{
|
||||
boundary_integs.Append(bfi);
|
||||
boundary_integs.Append (bfi);
|
||||
boundary_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi)
|
||||
{
|
||||
interior_face_integs.Append(bfi);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi)
|
||||
{
|
||||
boundary_face_integs.Append(bfi);
|
||||
boundary_face_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &bdr_marker)
|
||||
{
|
||||
boundary_face_integs.Append(bfi);
|
||||
boundary_face_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
trace_face_integs.Append (bfi);
|
||||
@@ -1607,108 +1587,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Array<int> trial_vdofs2, test_vdofs2;
|
||||
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
|
||||
|
||||
int nfaces = mesh->GetNumFaces();
|
||||
for (int i = 0; i < nfaces; i++)
|
||||
{
|
||||
ftr = mesh->GetInteriorFaceTransformations(i);
|
||||
if (ftr != NULL)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
if (ftr->Elem2No >= 0)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem2No, trial_vdofs2);
|
||||
test_fes->GetElementVDofs(ftr->Elem2No, test_vdofs2);
|
||||
trial_vdofs.Append(trial_vdofs2);
|
||||
test_vdofs.Append(test_vdofs2);
|
||||
trial_fe2 = trial_fes->GetFE(ftr->Elem2No);
|
||||
test_fe2 = test_fes->GetFE(ftr->Elem2No);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
// boundaries, but we can't dereference a NULL pointer, and we don't
|
||||
// want to actually make a fake element.
|
||||
trial_fe2 = trial_fe1;
|
||||
test_fe2 = test_fe1;
|
||||
}
|
||||
for (int k = 0; k < interior_face_integs.Size(); k++)
|
||||
{
|
||||
interior_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (boundary_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Array<int> tr_vdofs2, te_vdofs2;
|
||||
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
|
||||
|
||||
// Which boundary attributes need to be processed?
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (boundary_face_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *boundary_face_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary face integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < bdr_attr_marker.Size(); i++)
|
||||
{
|
||||
bdr_attr_marker[i] |= bdr_marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < trial_fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
ftr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (ftr != NULL)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
// boundaries, but we can't dereference a NULL pointer, and we don't
|
||||
// want to actually make a fake element.
|
||||
trial_fe2 = trial_fe1;
|
||||
test_fe2 = test_fe1;
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (boundary_face_integs_marker[k] &&
|
||||
(*boundary_face_integs_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
@@ -1889,11 +1767,10 @@ void MixedBilinearForm::ConformingAssemble()
|
||||
|
||||
void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
|
||||
{
|
||||
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
|
||||
const FiniteElement &test_fe = *test_fes->GetFE(i);
|
||||
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
|
||||
const FiniteElement &test_fe = *test_fes->GetFE(i);
|
||||
ElementTransformation *eltrans = test_fes->GetElementTransformation(i);
|
||||
domain_integs[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans,
|
||||
elmat);
|
||||
@@ -1906,21 +1783,19 @@ void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
|
||||
}
|
||||
else
|
||||
{
|
||||
const int tr_dofs = trial_fe.GetDof() * trial_fes->GetVDim();
|
||||
const int te_dofs = test_fe.GetDof() * test_fes->GetVDim();
|
||||
|
||||
elmat.SetSize(te_dofs, tr_dofs);
|
||||
trial_fes->GetElementVDofs(i, trial_vdofs);
|
||||
test_fes->GetElementVDofs(i, test_vdofs);
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
|
||||
{
|
||||
const FiniteElement &trial_be = *trial_fes->GetBE(i);
|
||||
const FiniteElement &test_be = *test_fes->GetBE(i);
|
||||
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
const FiniteElement &trial_be = *trial_fes->GetBE(i);
|
||||
const FiniteElement &test_be = *test_fes->GetBE(i);
|
||||
ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i);
|
||||
boundary_integs[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans,
|
||||
elmat);
|
||||
@@ -1933,103 +1808,9 @@ void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
|
||||
}
|
||||
else
|
||||
{
|
||||
const int tr_dofs = trial_be.GetDof() * trial_fes->GetVDim();
|
||||
const int te_dofs = test_be.GetDof() * test_fes->GetVDim();
|
||||
|
||||
elmat.SetSize(te_dofs, tr_dofs);
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void MixedBilinearForm::ComputeFaceMatrix(int i, DenseMatrix &elmat) const
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Mesh *mesh = test_fes -> GetMesh();
|
||||
ftr = mesh->GetFaceElementTransformations(i);
|
||||
MFEM_ASSERT(ftr, "No associated face transformations.");
|
||||
|
||||
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
|
||||
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
if (ftr->Elem2No >= 0)
|
||||
{
|
||||
trial_fe2 = trial_fes->GetFE(ftr->Elem2No);
|
||||
test_fe2 = test_fes->GetFE(ftr->Elem2No);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
// boundaries, but we can't dereference a NULL pointer, and we don't
|
||||
// want to actually make a fake element.
|
||||
trial_fe2 = trial_fe1;
|
||||
test_fe2 = test_fe1;
|
||||
}
|
||||
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
interior_face_integs[0]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elmat);
|
||||
for (int k = 1; k < interior_face_integs.Size(); k++)
|
||||
{
|
||||
interior_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int tr_dofs = trial_fe1->GetDof() * trial_fes->GetVDim();
|
||||
int te_dofs = test_fe1->GetDof() * test_fes->GetVDim();
|
||||
if (ftr->Elem2No >= 0)
|
||||
{
|
||||
tr_dofs += trial_fe2->GetDof() * trial_fes->GetVDim();
|
||||
te_dofs += test_fe2->GetDof() * test_fes->GetVDim();
|
||||
}
|
||||
|
||||
elmat.SetSize(te_dofs, tr_dofs);
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void MixedBilinearForm::ComputeBdrFaceMatrix(int i, DenseMatrix &elmat) const
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Mesh *mesh = test_fes -> GetMesh();
|
||||
ftr = mesh->GetBdrFaceTransformations(i);
|
||||
MFEM_ASSERT(ftr, "No associated boundary face.");
|
||||
|
||||
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
|
||||
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
// boundaries, but we can't dereference a NULL pointer, and we don't
|
||||
// want to actually make a fake element.
|
||||
trial_fe2 = trial_fe1;
|
||||
test_fe2 = test_fe1;
|
||||
|
||||
if (boundary_face_integs.Size())
|
||||
{
|
||||
boundary_face_integs[0]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elmat);
|
||||
for (int k = 1; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int tr_dofs = trial_fe1->GetDof() * trial_fes->GetVDim();
|
||||
const int te_dofs = test_fe1->GetDof() * test_fes->GetVDim();
|
||||
|
||||
elmat.SetSize(te_dofs, tr_dofs);
|
||||
trial_fes->GetBdrElementVDofs(i, trial_vdofs);
|
||||
test_fes->GetBdrElementVDofs(i, test_vdofs);
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
elmat = 0.0;
|
||||
}
|
||||
}
|
||||
@@ -2333,10 +2114,6 @@ MixedBilinearForm::~MixedBilinearForm()
|
||||
for (i = 0; i < domain_integs.Size(); i++) { delete domain_integs[i]; }
|
||||
for (i = 0; i < boundary_integs.Size(); i++)
|
||||
{ delete boundary_integs[i]; }
|
||||
for (i = 0; i < interior_face_integs.Size(); i++)
|
||||
{ delete interior_face_integs[i]; }
|
||||
for (i = 0; i < boundary_face_integs.Size(); i++)
|
||||
{ delete boundary_face_integs[i]; }
|
||||
for (i = 0; i < trace_face_integs.Size(); i++)
|
||||
{ delete trace_face_integs[i]; }
|
||||
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
|
||||
|
||||
@@ -772,14 +772,6 @@ protected:
|
||||
/// Entries are not owned.
|
||||
Array<Array<int>*> boundary_integs_marker;
|
||||
|
||||
/// Interior face integrators.
|
||||
Array<BilinearFormIntegrator*> interior_face_integs;
|
||||
|
||||
/// Boundary face integrators.
|
||||
Array<BilinearFormIntegrator*> boundary_face_integs;
|
||||
/// Entries are not owned.
|
||||
Array<Array<int>*> boundary_face_integs_marker;
|
||||
|
||||
/// Trace face (skeleton) integrators.
|
||||
Array<BilinearFormIntegrator*> trace_face_integs;
|
||||
|
||||
@@ -901,16 +893,6 @@ public:
|
||||
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
/// Adds an interior face integrator. Assumes ownership of @a bfi.
|
||||
void AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi);
|
||||
|
||||
/// Adds a boundary face integrator. Assumes ownership of @a bfi.
|
||||
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi);
|
||||
|
||||
/// Adds a boundary face integrator. Assumes ownership of @a bfi.
|
||||
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
|
||||
|
||||
This type of integrator assembles terms over all faces of the mesh using
|
||||
@@ -941,16 +923,6 @@ public:
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBBFI_Marker() { return &boundary_integs_marker; }
|
||||
|
||||
/// Access all integrators added with AddInteriorFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetFBFI() { return &interior_face_integs; }
|
||||
|
||||
/// Access all integrators added with AddBdrFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBFBFI() { return &boundary_face_integs; }
|
||||
/** @brief Access all boundary markers added with AddBdrFaceIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBFBFI_Marker() { return &boundary_face_integs_marker; }
|
||||
|
||||
/// Access all integrators added with AddTraceFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetTFBFI() { return &trace_face_integs; }
|
||||
|
||||
@@ -1016,13 +988,6 @@ public:
|
||||
/** @note The boundary attribute markers of the integrators are ignored. */
|
||||
void ComputeBdrTraceFaceMatrix(int i, DenseMatrix &elmat) const;
|
||||
|
||||
/// Compute the face matrix of the given face element
|
||||
void ComputeFaceMatrix(int i, DenseMatrix &elmat) const;
|
||||
|
||||
/// Compute the boundary face matrix of the given boundary element
|
||||
/** @note The boundary attribute markers of the integrators are ignored. */
|
||||
void ComputeBdrFaceMatrix(int i, DenseMatrix &elmat) const;
|
||||
|
||||
/// Assemble the given element matrix
|
||||
/** The element matrix @a elmat is assembled for the element @a i, i.e.
|
||||
added to the system matrix. The flag @a skip_zeros skips the zero
|
||||
|
||||
+6
-170
@@ -170,16 +170,6 @@ void BilinearFormIntegrator::AssembleFaceMatrix(
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &trial_fe1, const FiniteElement &test_fe1,
|
||||
const FiniteElement &trial_fe2, const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ABORT("AssembleFaceMatrix (mixed form) is not implemented for this"
|
||||
" Integrator class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &trial_face_fe, const FiniteElement &test_fe1,
|
||||
const FiniteElement &test_fe2, FaceElementTransformations &Trans,
|
||||
@@ -233,38 +223,28 @@ void TransposeIntegrator::SetIntRule(const IntegrationRule *ir)
|
||||
bfi->SetIntRule(ir);
|
||||
}
|
||||
|
||||
void TransposeIntegrator::AssembleElementMatrix(
|
||||
void TransposeIntegrator::AssembleElementMatrix (
|
||||
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
bfi->AssembleElementMatrix(el, Trans, bfi_elmat);
|
||||
bfi -> AssembleElementMatrix (el, Trans, bfi_elmat);
|
||||
// elmat = bfi_elmat^t
|
||||
elmat.Transpose (bfi_elmat);
|
||||
}
|
||||
|
||||
void TransposeIntegrator::AssembleElementMatrix2(
|
||||
void TransposeIntegrator::AssembleElementMatrix2 (
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
bfi->AssembleElementMatrix2(test_fe, trial_fe, Trans, bfi_elmat);
|
||||
bfi -> AssembleElementMatrix2 (test_fe, trial_fe, Trans, bfi_elmat);
|
||||
// elmat = bfi_elmat^t
|
||||
elmat.Transpose (bfi_elmat);
|
||||
}
|
||||
|
||||
void TransposeIntegrator::AssembleFaceMatrix(
|
||||
void TransposeIntegrator::AssembleFaceMatrix (
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
bfi->AssembleFaceMatrix(el1, el2, Trans, bfi_elmat);
|
||||
// elmat = bfi_elmat^t
|
||||
elmat.Transpose (bfi_elmat);
|
||||
}
|
||||
|
||||
void TransposeIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &tr_el1, const FiniteElement &te_el1,
|
||||
const FiniteElement &tr_el2, const FiniteElement &te_el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
bfi->AssembleFaceMatrix(te_el1, tr_el1, te_el2, tr_el2, Trans, bfi_elmat);
|
||||
bfi -> AssembleFaceMatrix (el1, el2, Trans, bfi_elmat);
|
||||
// elmat = bfi_elmat^t
|
||||
elmat.Transpose (bfi_elmat);
|
||||
}
|
||||
@@ -3518,150 +3498,6 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
|
||||
}
|
||||
}
|
||||
|
||||
void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &trial_fe1,
|
||||
const FiniteElement &test_fe1,
|
||||
const FiniteElement &trial_fe2,
|
||||
const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
int tr_ndof1, te_ndof1, tr_ndof2, te_ndof2;
|
||||
|
||||
real_t un, a, b, w;
|
||||
|
||||
dim = test_fe1.GetDim();
|
||||
tr_ndof1 = trial_fe1.GetDof();
|
||||
te_ndof1 = test_fe1.GetDof();
|
||||
Vector vu(dim), nor(dim);
|
||||
|
||||
if (Trans.Elem2No >= 0)
|
||||
{
|
||||
tr_ndof2 = trial_fe2.GetDof();
|
||||
te_ndof2 = test_fe2.GetDof();
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_ndof2 = 0;
|
||||
te_ndof2 = 0;
|
||||
}
|
||||
|
||||
tr_shape1.SetSize(tr_ndof1);
|
||||
te_shape1.SetSize(te_ndof1);
|
||||
tr_shape2.SetSize(tr_ndof2);
|
||||
te_shape2.SetSize(te_ndof2);
|
||||
elmat.SetSize(te_ndof1 + te_ndof2, tr_ndof1 + tr_ndof2);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order;
|
||||
// Assuming order(u)==order(mesh)
|
||||
if (Trans.Elem2No >= 0)
|
||||
order = (min(Trans.Elem1->OrderW(), Trans.Elem2->OrderW()) +
|
||||
max(trial_fe1.GetOrder(), trial_fe2.GetOrder()) +
|
||||
max(test_fe1.GetOrder(), test_fe2.GetOrder()));
|
||||
else
|
||||
{
|
||||
order = Trans.Elem1->OrderW() + trial_fe1.GetOrder() + test_fe1.GetOrder();
|
||||
}
|
||||
if (trial_fe1.Space() == FunctionSpace::Pk)
|
||||
{
|
||||
order++;
|
||||
}
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
IntegrationPoint eip1, eip2;
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
Trans.Elem1->SetIntPoint(&eip1);
|
||||
if (tr_ndof2 && te_ndof2)
|
||||
{
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
Trans.Elem2->SetIntPoint(&eip2);
|
||||
}
|
||||
trial_fe1.CalcPhysShape(*Trans.Elem1, tr_shape1);
|
||||
test_fe1.CalcPhysShape(*Trans.Elem1, te_shape1);
|
||||
|
||||
Trans.Face->SetIntPoint(&ip);
|
||||
|
||||
u->Eval(vu, *Trans.Elem1, eip1);
|
||||
|
||||
if (dim == 1)
|
||||
{
|
||||
nor(0) = 2*eip1.x - 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcOrtho(Trans.Face->Jacobian(), nor);
|
||||
}
|
||||
|
||||
un = vu * nor;
|
||||
a = 0.5 * alpha * un;
|
||||
b = beta * fabs(un);
|
||||
// note: if |alpha/2|==|beta| then |a|==|b|, i.e. (a==b) or (a==-b)
|
||||
// and therefore two blocks in the element matrix contribution
|
||||
// (from the current quadrature point) are 0
|
||||
|
||||
if (rho)
|
||||
{
|
||||
real_t rho_p;
|
||||
if (un >= 0.0 && tr_ndof2 && te_ndof2)
|
||||
{
|
||||
Trans.Elem2->SetIntPoint(&eip2);
|
||||
rho_p = rho->Eval(*Trans.Elem2, eip2);
|
||||
}
|
||||
else
|
||||
{
|
||||
rho_p = rho->Eval(*Trans.Elem1, eip1);
|
||||
}
|
||||
a *= rho_p;
|
||||
b *= rho_p;
|
||||
}
|
||||
|
||||
w = ip.weight * (a+b);
|
||||
if (w != 0.0)
|
||||
{
|
||||
for (int i = 0; i < te_ndof1; i++)
|
||||
for (int j = 0; j < tr_ndof1; j++)
|
||||
{
|
||||
elmat(i, j) += w * te_shape1(i) * tr_shape1(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (tr_ndof2 && te_ndof2)
|
||||
{
|
||||
trial_fe2.CalcPhysShape(*Trans.Elem2, tr_shape2);
|
||||
test_fe2.CalcPhysShape(*Trans.Elem2, te_shape2);
|
||||
|
||||
if (w != 0.0)
|
||||
for (int i = 0; i < te_ndof2; i++)
|
||||
for (int j = 0; j < tr_ndof1; j++)
|
||||
{
|
||||
elmat(te_ndof1+i, j) -= w * te_shape2(i) * tr_shape1(j);
|
||||
}
|
||||
|
||||
w = ip.weight * (b-a);
|
||||
if (w != 0.0)
|
||||
{
|
||||
for (int i = 0; i < te_ndof2; i++)
|
||||
for (int j = 0; j < tr_ndof2; j++)
|
||||
{
|
||||
elmat(te_ndof1+i, tr_ndof1+j) += w * te_shape2(i) * tr_shape2(j);
|
||||
}
|
||||
|
||||
for (int i = 0; i < te_ndof1; i++)
|
||||
for (int j = 0; j < tr_ndof2; j++)
|
||||
{
|
||||
elmat(i, tr_ndof1+j) -= w * te_shape1(i) * tr_shape2(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IntegrationRule &DGTraceIntegrator::GetRule(
|
||||
Geometry::Type geom, int order, FaceElementTransformations &T)
|
||||
|
||||
+5
-58
@@ -159,13 +159,6 @@ public:
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &trial_fe1,
|
||||
const FiniteElement &test_fe1,
|
||||
const FiniteElement &trial_fe2,
|
||||
const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Abstract method used for assembling TraceFaceIntegrators in a
|
||||
MixedBilinearForm. */
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &trial_face_fe,
|
||||
@@ -342,13 +335,6 @@ public:
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
void AssembleFaceMatrix(const FiniteElement &trial_fe1,
|
||||
const FiniteElement &test_fe1,
|
||||
const FiniteElement &trial_fe2,
|
||||
const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
void AssemblePA(const FiniteElementSpace& fes) override
|
||||
{
|
||||
bfi->AssemblePA(fes);
|
||||
@@ -3246,7 +3232,6 @@ protected:
|
||||
|
||||
private:
|
||||
Vector shape1, shape2;
|
||||
Vector tr_shape1, te_shape1, tr_shape2, te_shape2;
|
||||
|
||||
public:
|
||||
/// Construct integrator with $\rho = 1$, $\beta = \alpha/2$.
|
||||
@@ -3267,13 +3252,6 @@ public:
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
void AssembleFaceMatrix(const FiniteElement &trial_fe1,
|
||||
const FiniteElement &test_fe1,
|
||||
const FiniteElement &trial_fe2,
|
||||
const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
void AssemblePAInteriorFaces(const FiniteElementSpace &fes) override;
|
||||
|
||||
void AssemblePABoundaryFaces(const FiniteElementSpace &fes) override;
|
||||
@@ -3722,37 +3700,14 @@ private:
|
||||
the range space. Otherwise, a dof projection matrix is constructed. */
|
||||
class IdentityInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
protected:
|
||||
const int vdim;
|
||||
|
||||
public:
|
||||
/** @brief Construct an identity interpolator.
|
||||
|
||||
@param[in] vdim_ Vector dimension (number of components) in the domain
|
||||
and range FE spaces.
|
||||
*/
|
||||
IdentityInterpolator(int vdim_ = 1) : vdim(vdim_) { }
|
||||
IdentityInterpolator(): dofquad_fe(NULL) { }
|
||||
|
||||
void AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat) override
|
||||
{
|
||||
if (vdim == 1)
|
||||
{
|
||||
ran_fe.Project(dom_fe, Trans, elmat);
|
||||
return;
|
||||
}
|
||||
DenseMatrix elmat_block;
|
||||
ran_fe.Project(dom_fe, Trans, elmat_block);
|
||||
elmat.SetSize(vdim*elmat_block.Height(), vdim*elmat_block.Width());
|
||||
elmat = 0_r;
|
||||
for (int i = 0; i < vdim; i++)
|
||||
{
|
||||
elmat.SetSubMatrix(i*elmat_block.Height(), i*elmat_block.Width(),
|
||||
elmat_block);
|
||||
}
|
||||
}
|
||||
{ ran_fe.Project(dom_fe, Trans, elmat); }
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
@@ -3761,9 +3716,11 @@ public:
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
|
||||
virtual ~IdentityInterpolator() { delete dofquad_fe; }
|
||||
|
||||
private:
|
||||
/// 1D finite element that generates and owns the 1D DofToQuad maps below
|
||||
std::unique_ptr<FiniteElement> dofquad_fe;
|
||||
FiniteElement *dofquad_fe;
|
||||
|
||||
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
|
||||
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
|
||||
@@ -3773,16 +3730,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/** @brief Class identical to IdentityInterpolator with the exception that it
|
||||
requires the vector dimension (number of components) to be specified during
|
||||
construction. */
|
||||
class VectorIdentityInterpolator : public IdentityInterpolator
|
||||
{
|
||||
public:
|
||||
VectorIdentityInterpolator(int vdim_) : IdentityInterpolator(vdim_) { }
|
||||
};
|
||||
|
||||
|
||||
/** Class for constructing the (local) discrete curl matrix which can be used
|
||||
as an integrator in a DiscreteLinearOperator object to assemble the global
|
||||
discrete curl matrix. */
|
||||
|
||||
@@ -798,12 +798,6 @@ public:
|
||||
/// Sets coefficient in the vector.
|
||||
void Set(int i, Coefficient *c, bool own=true);
|
||||
|
||||
/// Set ownership of the i'th coefficient
|
||||
void SetOwnership(int i, bool own) { ownCoeff[i] = own; }
|
||||
|
||||
/// Get ownership of the i'th coefficient
|
||||
bool GetOwnership(int i) const { return ownCoeff[i]; }
|
||||
|
||||
/// Evaluates i'th component of the vector of coefficients and returns the
|
||||
/// value.
|
||||
real_t Eval(int i, ElementTransformation &T, const IntegrationPoint &ip)
|
||||
@@ -1326,12 +1320,6 @@ public:
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, int j, Coefficient * c, bool own=true);
|
||||
|
||||
/// Set ownership of the coefficient at (i,j) in the matrix
|
||||
void SetOwnership(int i, int j, bool own) { ownCoeff[i*width+j] = own; }
|
||||
|
||||
/// Get ownership of the coefficient at (i,j) in the matrix
|
||||
bool GetOwnership(int i, int j) const { return ownCoeff[i*width+j]; }
|
||||
|
||||
using MatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate coefficient located at (i,j) in the matrix using integration
|
||||
@@ -1372,12 +1360,6 @@ public:
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, VectorCoefficient * c, bool own=true);
|
||||
|
||||
/// Set ownership of the i'th coefficient
|
||||
void SetOwnership(int i, bool own) { ownCoeff[i] = own; }
|
||||
|
||||
/// Get ownership of the i'th coefficient
|
||||
bool GetOwnership(int i) const { return ownCoeff[i]; }
|
||||
|
||||
using MatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate coefficient located at the i-th row of the matrix using integration
|
||||
|
||||
+1
-1
@@ -1245,7 +1245,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
hypre_ParCSRMatrix *Aih = *Ah;
|
||||
Ah->HypreReadWrite();
|
||||
const int *d_ess_tdof_list =
|
||||
ess_tdof_list.GetMemory().Read(GetHypreForallMemoryClass(), n);
|
||||
ess_tdof_list.GetMemory().Read(GetHypreMemoryClass(), n);
|
||||
HYPRE_Int *d_diag_i = Aih->diag->i;
|
||||
real_t *d_diag_data = Aih->diag->data;
|
||||
mfem::hypre_forall(n, [=] MFEM_HOST_DEVICE (int k)
|
||||
|
||||
+5
-7
@@ -37,7 +37,7 @@ FindPointsGSLIB::FindPointsGSLIB()
|
||||
: mesh(NULL),
|
||||
fec_map_lin(NULL),
|
||||
fdata2D(NULL), fdata3D(NULL), cr(NULL), gsl_comm(NULL),
|
||||
dim(-1), points_cnt(-1), setupflag(false), default_interp_value(0),
|
||||
dim(-1), points_cnt(0), setupflag(false), default_interp_value(0),
|
||||
avgtype(AvgType::ARITHMETIC), bdr_tol(1e-8)
|
||||
{
|
||||
mesh_split.SetSize(4);
|
||||
@@ -85,7 +85,7 @@ FindPointsGSLIB::FindPointsGSLIB(MPI_Comm comm_)
|
||||
: mesh(NULL),
|
||||
fec_map_lin(NULL),
|
||||
fdata2D(NULL), fdata3D(NULL), cr(NULL), gsl_comm(NULL),
|
||||
dim(-1), points_cnt(-1), setupflag(false), default_interp_value(0),
|
||||
dim(-1), points_cnt(0), setupflag(false), default_interp_value(0),
|
||||
avgtype(AvgType::ARITHMETIC), bdr_tol(1e-8)
|
||||
{
|
||||
mesh_split.SetSize(4);
|
||||
@@ -307,7 +307,6 @@ void FindPointsGSLIB::FreeData()
|
||||
}
|
||||
if (fec_map_lin) { delete fec_map_lin; fec_map_lin = NULL; }
|
||||
setupflag = false;
|
||||
points_cnt = -1;
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::SetupSplitMeshes()
|
||||
@@ -898,8 +897,7 @@ 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());
|
||||
FiniteElementSpace fes(mesh, &fec, ncomp);
|
||||
GridFunction field_in_h1(&fes);
|
||||
|
||||
if (avgtype == AvgType::ARITHMETIC)
|
||||
@@ -929,7 +927,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
{
|
||||
for (int i = 0; i < indl2.Size(); i++)
|
||||
{
|
||||
int idx = field_in_h1.FESpace()->GetOrdering() == Ordering::byNODES?
|
||||
int idx = field_in.FESpace()->GetOrdering() == Ordering::byNODES ?
|
||||
indl2[i] + j*points_cnt:
|
||||
indl2[i]*ncomp + j;
|
||||
field_out(idx) = field_out_l2(idx);
|
||||
@@ -1174,7 +1172,7 @@ void FindPointsGSLIB::DistributePointInfoToOwningMPIRanks(
|
||||
Array<unsigned int> &recv_elem, Vector &recv_ref,
|
||||
Array<unsigned int> &recv_code)
|
||||
{
|
||||
MFEM_VERIFY(points_cnt >= 0,
|
||||
MFEM_VERIFY(points_cnt,
|
||||
"Invalid size. Please make sure to call FindPoints method "
|
||||
"before calling this function.");
|
||||
|
||||
|
||||
@@ -1819,12 +1819,10 @@ void IdentityInterpolator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
|
||||
MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
|
||||
|
||||
MFEM_VERIFY(vdim == 1, "vdim != 1 with PA is not supported yet!");
|
||||
|
||||
ne = trial_fes.GetNE();
|
||||
|
||||
const int order = trial_el->GetOrder();
|
||||
dofquad_fe.reset(new H1_SegmentElement(order));
|
||||
dofquad_fe = new H1_SegmentElement(order);
|
||||
mfem::QuadratureFunctions1D qf1d;
|
||||
mfem::IntegrationRule closed_ir;
|
||||
closed_ir.SetSize(order + 1);
|
||||
|
||||
+12
-230
@@ -31,172 +31,6 @@ void CutIntegrationRules::SetLevelSetProjectionOrder(int order)
|
||||
lsOrder = order;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
void AlgoimIntegrationRules::GetSurfaceIntegrationRule(ElementTransformation
|
||||
&Tr,
|
||||
IntegrationRule &result)
|
||||
{
|
||||
GenerateLSVector(Tr,LvlSet);
|
||||
|
||||
const int dim=pe->GetDim();
|
||||
int np1d=CutIntegrationRules::Order/2+1;
|
||||
if (dim==2)
|
||||
{
|
||||
LevelSet2D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
|
||||
2, -1, np1d);
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelSet3D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
|
||||
3, -1, np1d);
|
||||
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AlgoimIntegrationRules::GetVolumeIntegrationRule(ElementTransformation &Tr,
|
||||
IntegrationRule &result,
|
||||
const IntegrationRule *sir)
|
||||
{
|
||||
GenerateLSVector(Tr,LvlSet);
|
||||
|
||||
const int dim=pe->GetDim();
|
||||
int np1d=CutIntegrationRules::Order/2+1;
|
||||
if (dim==2)
|
||||
{
|
||||
LevelSet2D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
|
||||
-1, -1, np1d);
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelSet3D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
|
||||
-1, -1, np1d);
|
||||
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AlgoimIntegrationRules::GetSurfaceWeights(ElementTransformation &Tr,
|
||||
const IntegrationRule &sir,
|
||||
Vector &weights)
|
||||
{
|
||||
GenerateLSVector(Tr,LvlSet);
|
||||
|
||||
DenseMatrix bmat; // gradients of the shape functions in isoparametric space
|
||||
DenseMatrix pmat; // gradients of the shape functions in physical space
|
||||
Vector inormal; // normal to the level set in isoparametric space
|
||||
Vector tnormal; // normal to the level set in physical space
|
||||
bmat.SetSize(pe->GetDof(),pe->GetDim());
|
||||
pmat.SetSize(pe->GetDof(),pe->GetDim());
|
||||
inormal.SetSize(pe->GetDim());
|
||||
tnormal.SetSize(pe->GetDim());
|
||||
|
||||
weights.SetSize(sir.GetNPoints());
|
||||
|
||||
for (int j = 0; j < sir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = sir.IntPoint(j);
|
||||
Tr.SetIntPoint(&ip);
|
||||
pe->CalcDShape(ip,bmat);
|
||||
Mult(bmat, Tr.InverseJacobian(), pmat);
|
||||
// compute the normal to the LS in isoparametric space
|
||||
bmat.MultTranspose(lsvec,inormal);
|
||||
// compute the normal to the LS in physical space
|
||||
pmat.MultTranspose(lsvec,tnormal);
|
||||
weights[j]= tnormal.Norml2() / inormal.Norml2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AlgoimIntegrationRules::GenerateLSVector(ElementTransformation &Tr,
|
||||
Coefficient* lvlset)
|
||||
{
|
||||
//check if the coefficient is already projected
|
||||
if (currentElementNo==Tr.ElementNo)
|
||||
{
|
||||
if (currentLvlSet==lvlset)
|
||||
{
|
||||
if (currentGeometry==Tr.GetGeometryType())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentElementNo=Tr.ElementNo;
|
||||
|
||||
if (currentGeometry!=Tr.GetGeometryType())
|
||||
{
|
||||
delete le;
|
||||
delete pe;
|
||||
currentGeometry=Tr.GetGeometryType();
|
||||
if (Tr.GetGeometryType()==Geometry::Type::SQUARE)
|
||||
{
|
||||
pe=new H1Pos_QuadrilateralElement(lsOrder);
|
||||
le=new H1_QuadrilateralElement(lsOrder);
|
||||
}
|
||||
else if (Tr.GetGeometryType()==Geometry::Type::CUBE)
|
||||
{
|
||||
pe=new H1Pos_HexahedronElement(lsOrder);
|
||||
le=new H1_HexahedronElement(lsOrder);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Currently MFEM + Algoim supports only quads and hexes.");
|
||||
}
|
||||
|
||||
T.SetSize(pe->GetDof());
|
||||
pe->Project(*le,Tr,T);
|
||||
//The transformation matrix depends only on the geometry for change of basis
|
||||
}
|
||||
|
||||
currentLvlSet=lvlset;
|
||||
const IntegrationRule &ir=le->GetNodes();
|
||||
lsvec.SetSize(ir.GetNPoints());
|
||||
lsfun.SetSize(ir.GetNPoints());
|
||||
for (int i=0; i<ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
Tr.SetIntPoint(&ip);
|
||||
lsfun(i)=lvlset->Eval(Tr,ip);
|
||||
}
|
||||
T.Mult(lsfun,lsvec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
void MomentFittingIntRules::InitSurface(int order, Coefficient& levelset,
|
||||
@@ -341,7 +175,6 @@ void MomentFittingIntRules::ComputeFaceWeights(ElementTransformation& Tr)
|
||||
local_mesh.GetElementTransformation(0, &faceTrafo);
|
||||
|
||||
// The 3D face integrals are computed as 2D volumetric integrals.
|
||||
// The 2D face integrals are computed as 1D volumetric integrals.
|
||||
MomentFittingIntRules FaceRules(Order, *LvlSet, lsOrder);
|
||||
IntegrationRule FaceRule;
|
||||
FaceRules.GetVolumeIntegrationRule(faceTrafo, FaceRule);
|
||||
@@ -421,56 +254,8 @@ void MomentFittingIntRules::ComputeSurfaceWeights1D(ElementTransformation& Tr)
|
||||
}
|
||||
}
|
||||
|
||||
double bisect(ElementTransformation &Tr, Coefficient *LvlSet)
|
||||
{
|
||||
IntegrationPoint intp;
|
||||
|
||||
IntegrationPoint ip0;
|
||||
ip0.x = 0.;
|
||||
IntegrationPoint ip1;
|
||||
ip1.x = 1.;
|
||||
Tr.SetIntPoint(&ip0);
|
||||
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip1) < 0.)
|
||||
{
|
||||
IntegrationPoint ip2;
|
||||
ip2.x = .5;
|
||||
while (LvlSet->Eval(Tr, ip2) > 1e-12
|
||||
|| LvlSet->Eval(Tr, ip2) < -1e-12)
|
||||
{
|
||||
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip2) < 0.)
|
||||
{
|
||||
ip1.x = ip2.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
ip0.x = ip2.x;
|
||||
}
|
||||
|
||||
ip2.x = (ip1.x + ip0.x) / 2.;
|
||||
}
|
||||
intp.x = ip2.x;
|
||||
intp.weight = 1. / Tr.Weight();
|
||||
}
|
||||
else if (LvlSet->Eval(Tr, ip0) > 0. && LvlSet->Eval(Tr, ip1) <= 1e-12)
|
||||
{
|
||||
intp.x = 1.;
|
||||
intp.weight = 1. / Tr.Weight();
|
||||
}
|
||||
else if (LvlSet->Eval(Tr, ip1) > 0. && LvlSet->Eval(Tr, ip0) <= 1e-12)
|
||||
{
|
||||
intp.x = 0.;
|
||||
intp.weight = 1. / Tr.Weight();
|
||||
}
|
||||
else
|
||||
{
|
||||
intp.x = .5;
|
||||
intp.weight = 0.;
|
||||
}
|
||||
|
||||
return intp.x;
|
||||
}
|
||||
|
||||
void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr)
|
||||
void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr,
|
||||
const IntegrationRule* sir)
|
||||
{
|
||||
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
|
||||
IntegrationRule ir2 = irs.Get(Geometry::SEGMENT, ir.GetOrder());
|
||||
@@ -486,7 +271,7 @@ void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr)
|
||||
real_t length;
|
||||
if (LvlSet->Eval(Tr, ip0) > 0.)
|
||||
{
|
||||
length = bisect(Tr, LvlSet);
|
||||
length = sir->IntPoint(0).x;
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = ir.IntPoint(ip);
|
||||
@@ -496,11 +281,11 @@ void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr)
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 1. - bisect(Tr, LvlSet);
|
||||
length = 1. - sir->IntPoint(0).x;
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = ir.IntPoint(ip);
|
||||
intp.x = bisect(Tr, LvlSet) + ir2.IntPoint(ip).x * length;
|
||||
intp.x = sir->IntPoint(ip).x + ir2.IntPoint(ip).x * length;
|
||||
intp.weight = ir2.IntPoint(ip).weight * length;
|
||||
}
|
||||
}
|
||||
@@ -1706,29 +1491,26 @@ void MomentFittingIntRules::GetVolumeIntegrationRule(ElementTransformation& Tr,
|
||||
}
|
||||
|
||||
IntegrationRule SIR;
|
||||
|
||||
if (Tr.GetDimension() == 1)
|
||||
{
|
||||
Clear();
|
||||
InitVolume(Order, *LvlSet, lsOrder, Tr);
|
||||
}
|
||||
else if (sir == NULL)
|
||||
if (sir == NULL)
|
||||
{
|
||||
Order++;
|
||||
GetSurfaceIntegrationRule(Tr, SIR);
|
||||
Order--;
|
||||
}
|
||||
else if (sir->GetOrder() - 1 != ir.GetOrder())
|
||||
else if ((sir->GetOrder() - 1) != ir.GetOrder())
|
||||
{
|
||||
Order++;
|
||||
GetSurfaceIntegrationRule(Tr, SIR);
|
||||
Order--;
|
||||
}
|
||||
else { SIR = *sir; }
|
||||
else
|
||||
{
|
||||
SIR = *sir;
|
||||
}
|
||||
|
||||
if (Tr.GetDimension() == 1)
|
||||
{
|
||||
ComputeVolumeWeights1D(Tr);
|
||||
ComputeVolumeWeights1D(Tr, &SIR);
|
||||
}
|
||||
else if (Tr.GetDimension() == 2)
|
||||
{
|
||||
|
||||
+3
-354
@@ -18,16 +18,6 @@
|
||||
#include "eltrans.hpp"
|
||||
#include "coefficient.hpp"
|
||||
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
#include <algoim_quad.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/**
|
||||
@@ -126,349 +116,6 @@ public:
|
||||
virtual ~CutIntegrationRules() {}
|
||||
};
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
// define templated element bases
|
||||
namespace TmplPoly_1D
|
||||
{
|
||||
|
||||
/// Templated version of CalcBinomTerms
|
||||
template<typename float_type>
|
||||
void CalcBinomTerms(const int p, const float_type x, const float_type y,
|
||||
float_type* u)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = float_type(1.);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Poly_1D::Binom(p);
|
||||
float_type z = x;
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
u[i] = b[i]*z;
|
||||
z *= x;
|
||||
}
|
||||
u[p] = z;
|
||||
z = y;
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
u[i] *= z;
|
||||
z *= y;
|
||||
}
|
||||
u[0] = z;
|
||||
}
|
||||
}
|
||||
|
||||
/// Templated version of CalcBinomTerms
|
||||
template<typename float_type>
|
||||
void CalcBinomTerms(const int p, const float_type x, const float_type y,
|
||||
float_type* u, float_type* d)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = float_type(1.);
|
||||
d[0] = float_type(0.);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Poly_1D::Binom(p);
|
||||
const float_type xpy = x + y, ptx = p*x;
|
||||
float_type z = float_type(1.);
|
||||
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
d[i] = b[i]*z*(i*xpy - ptx);
|
||||
z *= x;
|
||||
u[i] = b[i]*z;
|
||||
}
|
||||
d[p] = p*z;
|
||||
u[p] = z*x;
|
||||
z = float_type(1.);
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
d[i] *= z;
|
||||
z *= y;
|
||||
u[i] *= z;
|
||||
}
|
||||
d[0] = -p*z;
|
||||
u[0] = z*y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Templated evaluation of Bernstein basis
|
||||
template <typename float_type>
|
||||
void CalcBernstein(const int p, const float_type x, float_type *u)
|
||||
{
|
||||
CalcBinomTerms(p, x, 1. - x, u);
|
||||
}
|
||||
|
||||
|
||||
/// Templated evaluation of Bernstein basis
|
||||
template <typename float_type>
|
||||
void CalcBernstein(const int p, const float_type x,
|
||||
float_type *u, float_type *d)
|
||||
{
|
||||
CalcBinomTerms(p, x, 1. - x, u, d);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AlgoimIntegrationRules : public CutIntegrationRules
|
||||
{
|
||||
public:
|
||||
|
||||
/** @brief Constructor to set up the generated cut IntegrationRules.
|
||||
|
||||
@param [in] order Order of the constructed IntegrationRule.
|
||||
@param [in] lvlset Coefficient whose zero level set specifies the cut.
|
||||
@param [in] lsO Polynomial degree for projecting the level-set
|
||||
Coefficient to a GridFunction, which is used to
|
||||
compute gradients and normals. */
|
||||
AlgoimIntegrationRules(int order, Coefficient &lvlset, int lsO = 2)
|
||||
: CutIntegrationRules(order, lvlset, lsO)
|
||||
{
|
||||
pe=nullptr;
|
||||
le=nullptr;
|
||||
currentLvlSet=nullptr;
|
||||
currentGeometry=Geometry::Type::INVALID;
|
||||
currentElementNo = -1;
|
||||
}
|
||||
|
||||
virtual ~AlgoimIntegrationRules()
|
||||
{
|
||||
delete pe;
|
||||
delete le;
|
||||
}
|
||||
|
||||
virtual void SetOrder(int order) override
|
||||
{
|
||||
MFEM_VERIFY(order > 0, "Invalid input");
|
||||
Order = order;
|
||||
delete pe;
|
||||
delete le;
|
||||
pe=nullptr;
|
||||
le=nullptr;
|
||||
currentLvlSet=nullptr;
|
||||
currentGeometry=Geometry::Type::INVALID;
|
||||
currentElementNo=-1;
|
||||
}
|
||||
|
||||
virtual void SetLevelSetProjectionOrder(int order) override
|
||||
{
|
||||
MFEM_VERIFY(order > 0, "Invalid input");
|
||||
lsOrder = order;
|
||||
delete pe;
|
||||
delete le;
|
||||
pe=nullptr;
|
||||
le=nullptr;
|
||||
currentLvlSet=nullptr;
|
||||
currentGeometry=Geometry::Type::INVALID;
|
||||
currentElementNo=-1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Construct a cut-surface IntegrationRule.
|
||||
|
||||
Construct an IntegrationRule to integrate on the surface given by the
|
||||
already specified level set function, for the element given by @a Tr.
|
||||
|
||||
@param [in] Tr Specifies the IntegrationRule's associated mesh element.
|
||||
@param [out] result IntegrationRule on the cut-surface
|
||||
*/
|
||||
virtual
|
||||
void GetSurfaceIntegrationRule(ElementTransformation &Tr,
|
||||
IntegrationRule &result) override;
|
||||
|
||||
/**
|
||||
@brief Construct a cut-volume IntegrationRule.
|
||||
|
||||
Construct an IntegrationRule to integrate in the subdomain given by the
|
||||
positive values of the already specified level set function, for the element
|
||||
given by @a Tr.
|
||||
|
||||
@param [in] Tr Specifies the IntegrationRule's associated mesh element.
|
||||
@param [out] result IntegrationRule for the cut-volume
|
||||
@param [in] sir Corresponding IntegrationRule for the surface, which can
|
||||
be used to avoid computations.
|
||||
*/
|
||||
virtual
|
||||
void GetVolumeIntegrationRule(ElementTransformation &Tr,
|
||||
IntegrationRule &result,
|
||||
const IntegrationRule *sir = nullptr) override;
|
||||
|
||||
|
||||
/**
|
||||
@brief Compute transformation quadrature weights for surface integration.
|
||||
|
||||
Compute the transformation weights for integration over the cut-surface in
|
||||
reference space.
|
||||
|
||||
@param [in] Tr Specifies the IntegrationRule's associated element.
|
||||
@param [in] sir IntegrationRule defining the IntegrationPoints
|
||||
@param [out] weights Vector containing the transformation weights.
|
||||
*/
|
||||
virtual
|
||||
void GetSurfaceWeights(ElementTransformation &Tr,
|
||||
const IntegrationRule &sir,
|
||||
Vector &weights) override;
|
||||
|
||||
private:
|
||||
|
||||
/// projects the lvlset coefficient onto the lsvec,
|
||||
/// i.e., represent the level-set using Bernstein bases
|
||||
void GenerateLSVector(ElementTransformation &Tr, Coefficient* lvlset);
|
||||
|
||||
|
||||
/// Lagrange finite element used for converting coefficients to positive basis
|
||||
FiniteElement* le;
|
||||
PositiveTensorFiniteElement *pe;
|
||||
DenseMatrix T; //Projection matrix from nodal basis to positive basis
|
||||
Vector lsvec; // level-set in Bernstein basis
|
||||
Vector lsfun; // level-set in nodal basis
|
||||
Geometry::Type currentGeometry; // the current element geometry
|
||||
Coefficient* currentLvlSet; //the current level-set coefficient
|
||||
int currentElementNo; //the current element No
|
||||
|
||||
/// 3D level-set function object required by Algoim.
|
||||
struct LevelSet3D
|
||||
{
|
||||
/// Constructor for 3D level-set function object required by Algoim.
|
||||
LevelSet3D(PositiveTensorFiniteElement* el_, Vector& lsfun_)
|
||||
: el(el_), lsfun(lsfun_) { }
|
||||
|
||||
/// Returns the value of the LSF for point x.
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[2], u3);
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
T res=T(0.0);
|
||||
for (int oo = 0, kk = 0; kk <= el_order; kk++)
|
||||
for (int jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res=res-u1[ii]*u2[jj]*u3[kk]*lsfun(dof_map[oo++]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns the gradients of the LSF for point x.
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,3> grad(const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
T d3[el_order+1];
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[2], u3, d3);
|
||||
|
||||
blitz::TinyVector<T,3> res(T(0.0),T(0.0),T(0.0));
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
for (int oo = 0, kk = 0; kk <= el_order; kk++)
|
||||
for (int jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res[0]=res[0]-d1[ii]*u2[jj]*u3[kk]*lsfun(dof_map[oo]);
|
||||
res[1]=res[1]-u1[ii]*d2[jj]*u3[kk]*lsfun(dof_map[oo]);
|
||||
res[2]=res[2]-u1[ii]*u2[jj]*d3[kk]*lsfun(dof_map[oo]);
|
||||
oo++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
PositiveTensorFiniteElement* el;
|
||||
Vector& lsfun;
|
||||
};
|
||||
|
||||
/// 2D level-set function object required by Algoim.
|
||||
struct LevelSet2D
|
||||
{
|
||||
/// Constructor for 2D level-set function object required by Algoim.
|
||||
LevelSet2D(PositiveTensorFiniteElement* el_, Vector& lsfun_)
|
||||
:el(el_), lsfun(lsfun_) { }
|
||||
|
||||
/// Returns the value of the LSF for point x.
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
T res=T(0.0);
|
||||
|
||||
for (int oo = 0, jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res=res-u1[ii]*u2[jj]*lsfun(dof_map[oo++]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns the gradients of the LSF for point x.
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,2> grad(const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
|
||||
blitz::TinyVector<T,2> res(T(0.0),T(0.0));
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
for (int oo = 0, jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res[0]=res[0]-(d1[ii]*u2[jj])*lsfun(dof_map[oo]);
|
||||
res[1]=res[1]-(u1[ii]*d2[jj])*lsfun(dof_map[oo]);
|
||||
oo++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
PositiveTensorFiniteElement* el;
|
||||
Vector& lsfun;
|
||||
};
|
||||
};
|
||||
#endif //MFEM_USE_ALGOIM
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
/**
|
||||
@@ -565,8 +212,10 @@ protected:
|
||||
rule.
|
||||
|
||||
@param [in] Tr ElementTransformation of the current element
|
||||
@param [in] sir corresponding IntegrationRule on surface
|
||||
*/
|
||||
void ComputeVolumeWeights1D(ElementTransformation& Tr);
|
||||
void ComputeVolumeWeights1D(ElementTransformation& Tr,
|
||||
const IntegrationRule* sir);
|
||||
|
||||
/**
|
||||
@brief Compute 2D quadrature weights
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
{
|
||||
std::tuple<Params...> param_tuple(PARAMS...);
|
||||
Kernels::Get().table[param_tuple] =
|
||||
Kernels:: template Kernel<PARAMS..., OptParams{}...>();
|
||||
Kernels:: template Kernel<PARAMS...>();
|
||||
};
|
||||
// Version with optional parameters
|
||||
template <OptParams... OPT_PARAMS>
|
||||
|
||||
+13
-7
@@ -242,13 +242,13 @@ void BatchedLOR_AMS::FormGradientMatrix()
|
||||
template <typename T>
|
||||
static inline const T *HypreRead(const Memory<T> &mem)
|
||||
{
|
||||
return mem.Read(GetHypreForallMemoryClass(), mem.Capacity());
|
||||
return mem.Read(GetHypreMemoryClass(), mem.Capacity());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T *HypreWrite(Memory<T> &mem)
|
||||
{
|
||||
return mem.Write(GetHypreForallMemoryClass(), mem.Capacity());
|
||||
return mem.Write(GetHypreMemoryClass(), mem.Capacity());
|
||||
}
|
||||
|
||||
void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
@@ -278,7 +278,10 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
const int sdim = vert_fes.GetMesh()->SpaceDimension();
|
||||
const int ntdofs = R->Height();
|
||||
|
||||
xyz_tvec = new Vector(ntdofs*sdim, GetHypreMemoryType());
|
||||
const MemoryClass mc = GetHypreMemoryClass();
|
||||
bool dev = (mc == MemoryClass::DEVICE);
|
||||
|
||||
xyz_tvec = new Vector(ntdofs*sdim);
|
||||
|
||||
auto xyz_tv = Reshape(HypreWrite(xyz_tvec->GetMemory()), ntdofs, sdim);
|
||||
const auto xyz_e =
|
||||
@@ -301,12 +304,15 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
// Make x, y, z HypreParVectors point to T-vector data
|
||||
HYPRE_BigInt glob_size = vert_fes.GlobalTrueVSize();
|
||||
HYPRE_BigInt *cols = vert_fes.GetTrueDofOffsets();
|
||||
MPI_Comm comm = vert_fes.GetComm();
|
||||
x = new HypreParVector(comm, glob_size, *xyz_tvec, 0*ntdofs, cols);
|
||||
y = new HypreParVector(comm, glob_size, *xyz_tvec, 1*ntdofs, cols);
|
||||
|
||||
real_t *d_x_ptr = xyz_tv + 0*ntdofs;
|
||||
x = new HypreParVector(vert_fes.GetComm(), glob_size, d_x_ptr, cols, dev);
|
||||
real_t *d_y_ptr = xyz_tv + 1*ntdofs;
|
||||
y = new HypreParVector(vert_fes.GetComm(), glob_size, d_y_ptr, cols, dev);
|
||||
if (sdim == 3)
|
||||
{
|
||||
z = new HypreParVector(comm, glob_size, *xyz_tvec, 2*ntdofs, cols);
|
||||
real_t *d_z_ptr = xyz_tv + 2*ntdofs;
|
||||
z = new HypreParVector(vert_fes.GetComm(), glob_size, d_z_ptr, cols, dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (c) 2010-2023, 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_MDGRIDFUNC
|
||||
#define MFEM_MDGRIDFUNC
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#include "fem/gridfunc.hpp"
|
||||
#include "general/mdspan.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int N, class Layout = MDLayoutLeft<N>>
|
||||
class MDGridFunction : public MDSpan<GridFunction, N, Layout>
|
||||
{
|
||||
using base_t = MDSpan<GridFunction, N, Layout>;
|
||||
using base_t::Nd;
|
||||
using base_t::Sd;
|
||||
using GridFunction::data;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief MDGridFunction default constructor (recursion)
|
||||
*/
|
||||
MDGridFunction(): base_t() { }
|
||||
|
||||
/**
|
||||
* @brief MDGridFunction recursion constructor
|
||||
* @param[in] fes Finite element space to use
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDGridFunction(FiniteElementSpace *fes, Ts... args): MDGridFunction(args...)
|
||||
{
|
||||
SetSpace(fes);
|
||||
MFEM_VERIFY(fes->GetVDim() == 1,
|
||||
"Only FiniteElementSpace with vdim of 1 are supported");
|
||||
base_t::Setup(fes->GetNDofs(), args...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MDGridFunction recursion constructor
|
||||
* @param[in] dim Dimension indice
|
||||
* @param[in] args Rest of dimension indices or finite element space to use
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDGridFunction(int dim, Ts... args): MDGridFunction(args...)
|
||||
{
|
||||
base_t::Setup(dim, args...);
|
||||
}
|
||||
|
||||
/// Move constructor not supported
|
||||
MDGridFunction(MDGridFunction&&) = delete;
|
||||
|
||||
/// Copy constructor not supported
|
||||
MDGridFunction(const MDGridFunction&) = delete;
|
||||
|
||||
/// Move assignment not supported
|
||||
MDGridFunction& operator=(MDGridFunction&&) = delete;
|
||||
|
||||
/// Copy assignment not supported
|
||||
MDGridFunction& operator=(const MDGridFunction&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Returns the specific GridFunction from dimension indices
|
||||
* @param[out] gf Returned GridFunction
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void GetScalarGridFunction(GridFunction &gf, Ts... args) const
|
||||
{
|
||||
FiniteElementSpace *fes = GridFunction::fes;
|
||||
MFEM_VERIFY(fes->GetNDofs() == Nd[n-1], "Error in dofs size!");
|
||||
gf.SetSpace(fes);
|
||||
for (int s = 0; s < Nd[n-1]; s++)
|
||||
{
|
||||
gf[s] = data[get_vdofs_offset +
|
||||
MDOffset<n,N,int,Ts...>::offset(Sd, s, args...)];
|
||||
}
|
||||
get_vdofs_offset = 0; // re-init for next calls
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the specific GridFunction from dimension indices
|
||||
* @param[in] dim Dimension indice
|
||||
* @param args Rest of dimension indices or GridFunction to be returned
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void GetScalarGridFunction(int dim, Ts&&... args) const
|
||||
{
|
||||
get_vdofs_offset += dim * Sd[n-1];
|
||||
MDGridFunction::GetScalarGridFunction<n+1>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the given GridFunction at the specific dimension indices
|
||||
* @param[in] gf GridFunction to set
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void SetScalarGridFunction(const GridFunction &gf, Ts... args)
|
||||
{
|
||||
MFEM_VERIFY(GridFunction::fes->GetNDofs() == Nd[n-1], "Error in dofs size!");
|
||||
for (int s = 0; s < Nd[n-1]; s++)
|
||||
{
|
||||
data[get_vdofs_offset +
|
||||
MDOffset<n,N,int,Ts...>::offset(Sd, s, args...)] = gf[s];
|
||||
}
|
||||
get_vdofs_offset = 0; // re-init for next calls
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the given GridFunction at the specific dimension indices
|
||||
* @param[in] dim Dimension indice
|
||||
* @param args Rest of dimension indices or given GridFunction to be used
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void SetScalarGridFunction(int dim, Ts... args)
|
||||
{
|
||||
get_vdofs_offset += dim * Sd[n-1];
|
||||
MDGridFunction::SetScalarGridFunction<n+1>(args...);
|
||||
}
|
||||
|
||||
using GridFunction::Read;
|
||||
using GridFunction::Write;
|
||||
using GridFunction::ReadWrite;
|
||||
using GridFunction::HostRead;
|
||||
using GridFunction::HostWrite;
|
||||
using GridFunction::HostReadWrite;
|
||||
|
||||
using GridFunction::GetData;
|
||||
using GridFunction::SetData;
|
||||
using GridFunction::SetSpace;
|
||||
|
||||
using Vector::operator=;
|
||||
|
||||
private:
|
||||
mutable int get_vdofs_offset = 0;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDGRIDFUNC
|
||||
+9
-229
@@ -43,13 +43,12 @@ static void Derivatives1D(const int NE,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b_);
|
||||
const int SDIM = GRAD_PHYS ? sdim : 1;
|
||||
const auto g = Reshape(g_, q1d, d1d);
|
||||
const auto j = Reshape(j_, q1d, SDIM, NE);
|
||||
const auto j = Reshape(j_, q1d, sdim, NE);
|
||||
const auto x = Reshape(x_, d1d, vdim, NE);
|
||||
auto y = Q_LAYOUT == QVectorLayout::byNODES ?
|
||||
Reshape(y_, q1d, vdim, SDIM, NE):
|
||||
Reshape(y_, vdim, SDIM, q1d, NE);
|
||||
Reshape(y_, q1d, vdim, sdim, NE):
|
||||
Reshape(y_, vdim, sdim, q1d, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
@@ -64,8 +63,8 @@ static void Derivatives1D(const int NE,
|
||||
}
|
||||
if (GRAD_PHYS)
|
||||
{
|
||||
if (SDIM == 1) { du[0] /= j(q, 0, e); }
|
||||
else if (SDIM == 2)
|
||||
if (sdim == 1) { du[0] /= j(q, 0, e); }
|
||||
else if (sdim == 2)
|
||||
{
|
||||
const real_t Jloc[2] = {j(q,0,e), j(q,1,e)};
|
||||
real_t Jinv[3];
|
||||
@@ -75,7 +74,7 @@ static void Derivatives1D(const int NE,
|
||||
du[0] = U;
|
||||
du[1] = V;
|
||||
}
|
||||
else // SDIM == 3
|
||||
else // sdim == 3
|
||||
{
|
||||
const real_t Jloc[3] = {j(q,0,e), j(q,1,e), j(q,2,e)};
|
||||
real_t Jinv[3];
|
||||
@@ -88,7 +87,7 @@ static void Derivatives1D(const int NE,
|
||||
du[2] = W;
|
||||
}
|
||||
}
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
for (int d = 0; d < sdim; ++d)
|
||||
{
|
||||
if (Q_LAYOUT == QVectorLayout::byVDIM) { y(c, d, q, e) = du[d]; }
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES) { y(q, c, d, e) = du[d]; }
|
||||
@@ -373,222 +372,14 @@ static void Derivatives3D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
|
||||
static void CollocatedDerivatives1D(const int NE,
|
||||
const real_t *g_,
|
||||
const real_t *j_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int sdim,
|
||||
const int vdim,
|
||||
const int d1d)
|
||||
{
|
||||
Derivatives1D<Q_LAYOUT, GRAD_PHYS>(
|
||||
NE, nullptr, g_, j_, x_, y_, sdim, vdim, d1d, d1d);
|
||||
}
|
||||
|
||||
// Template compute kernel for derivatives in 2D: tensor product version.
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
|
||||
int T_VDIM = 0, int T_D1D = 0,
|
||||
int T_NBZ = 1>
|
||||
static void CollocatedDerivatives2D(const int NE,
|
||||
const real_t *g_,
|
||||
const real_t *j_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int sdim = 2,
|
||||
const int vdim = 0,
|
||||
const int d1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
const int SDIM = GRAD_PHYS ? sdim : 2;
|
||||
static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
|
||||
|
||||
const auto g = Reshape(g_, D1D, D1D);
|
||||
const auto j = Reshape(j_, D1D, D1D, SDIM, 2, NE);
|
||||
const auto x = Reshape(x_, D1D, D1D, VDIM, NE);
|
||||
auto y = Q_LAYOUT == QVectorLayout:: byNODES ?
|
||||
Reshape(y_, D1D, D1D, VDIM, SDIM, NE):
|
||||
Reshape(y_, VDIM, SDIM, D1D, D1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, D1D, D1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
MFEM_SHARED real_t XY[NBZ][MD1*MD1];
|
||||
DeviceTensor<2> X((real_t*)(XY+tidz), D1D, D1D);
|
||||
|
||||
for (int c = 0; c < VDIM; ++c)
|
||||
{
|
||||
kernels::internal::LoadX<MD1,NBZ>(e,D1D,c,x,XY);
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
real_t w = 0.0;
|
||||
for (int dxy = 0; dxy < D1D; ++dxy)
|
||||
{
|
||||
u += X(dxy, dy) * g(dx,dxy);
|
||||
v += X(dx, dxy) * g(dy,dxy);
|
||||
}
|
||||
|
||||
if (GRAD_PHYS)
|
||||
{
|
||||
if (SDIM == 2)
|
||||
{
|
||||
real_t Jloc[4], Jinv[4];
|
||||
Jloc[0] = j(dx,dy,0,0,e);
|
||||
Jloc[1] = j(dx,dy,1,0,e);
|
||||
Jloc[2] = j(dx,dy,0,1,e);
|
||||
Jloc[3] = j(dx,dy,1,1,e);
|
||||
kernels::CalcInverse<2>(Jloc, Jinv);
|
||||
const real_t U = Jinv[0]*u + Jinv[1]*v;
|
||||
const real_t V = Jinv[2]*u + Jinv[3]*v;
|
||||
u = U;
|
||||
v = V;
|
||||
}
|
||||
else
|
||||
{
|
||||
real_t Jloc[6], Jinv[6];
|
||||
Jloc[0] = j(dx,dy,0,0,e);
|
||||
Jloc[1] = j(dx,dy,1,0,e);
|
||||
Jloc[2] = j(dx,dy,2,0,e);
|
||||
Jloc[3] = j(dx,dy,0,1,e);
|
||||
Jloc[4] = j(dx,dy,1,1,e);
|
||||
Jloc[5] = j(dx,dy,2,1,e);
|
||||
kernels::CalcLeftInverse<3,2>(Jloc, Jinv);
|
||||
const real_t U = Jinv[0]*u + Jinv[1]*v;
|
||||
const real_t V = Jinv[2]*u + Jinv[3]*v;
|
||||
const real_t W = Jinv[4]*u + Jinv[5]*v;
|
||||
u = U;
|
||||
v = V;
|
||||
w = W;
|
||||
}
|
||||
}
|
||||
|
||||
if (Q_LAYOUT == QVectorLayout::byVDIM)
|
||||
{
|
||||
y(c,0,dx,dy,e) = u;
|
||||
y(c,1,dx,dy,e) = v;
|
||||
if (SDIM == 3) { y(c,2,dx,dy,e) = w; }
|
||||
}
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES)
|
||||
{
|
||||
y(dx,dy,c,0,e) = u;
|
||||
y(dx,dy,c,1,e) = v;
|
||||
if (SDIM == 3) { y(dx,dy,c,2,e) = w; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Template compute kernel for derivatives in 3D: tensor product version.
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
|
||||
int T_VDIM = 0, int T_D1D = 0>
|
||||
static void CollocatedDerivatives3D(const int NE,
|
||||
const real_t *g_,
|
||||
const real_t *j_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int sdim = 3,
|
||||
const int vdim = 0,
|
||||
const int d1d = 0)
|
||||
{
|
||||
MFEM_VERIFY(sdim == 3, "");
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
|
||||
const auto g = Reshape(g_, D1D, D1D);
|
||||
const auto j = Reshape(j_, D1D, D1D, D1D, 3, 3, NE);
|
||||
const auto x = Reshape(x_, D1D, D1D, D1D, VDIM, NE);
|
||||
auto y = Q_LAYOUT == QVectorLayout:: byNODES ?
|
||||
Reshape(y_, D1D, D1D, D1D, VDIM, 3, NE):
|
||||
Reshape(y_, VDIM, 3, D1D, D1D, D1D, NE);
|
||||
|
||||
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_INTERP_1D;
|
||||
|
||||
MFEM_SHARED real_t uvw[MD1*MD1*MD1];
|
||||
DeviceTensor<3> X(uvw, D1D, D1D, D1D);
|
||||
|
||||
for (int c = 0; c < VDIM; ++c)
|
||||
{
|
||||
kernels::internal::LoadX(e,D1D,c,x,X);
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
real_t w = 0.0;
|
||||
for (int dxyz = 0; dxyz < D1D; ++dxyz)
|
||||
{
|
||||
u += X(dxyz, dy, dz) * g(dx,dxyz);
|
||||
v += X(dx, dxyz, dz) * g(dy,dxyz);
|
||||
w += X(dx, dy, dxyz) * g(dz,dxyz);
|
||||
}
|
||||
|
||||
if (GRAD_PHYS)
|
||||
{
|
||||
real_t Jloc[9], Jinv[9];
|
||||
for (int col = 0; col < 3; col++)
|
||||
{
|
||||
for (int row = 0; row < 3; row++)
|
||||
{
|
||||
Jloc[row+3*col] = j(dx,dy,dz,row,col,e);
|
||||
}
|
||||
}
|
||||
kernels::CalcInverse<3>(Jloc, Jinv);
|
||||
const real_t U = Jinv[0]*u + Jinv[1]*v + Jinv[2]*w;
|
||||
const real_t V = Jinv[3]*u + Jinv[4]*v + Jinv[5]*w;
|
||||
const real_t W = Jinv[6]*u + Jinv[7]*v + Jinv[8]*w;
|
||||
u = U; v = V; w = W;
|
||||
}
|
||||
if (Q_LAYOUT == QVectorLayout::byVDIM)
|
||||
{
|
||||
y(c,0,dx,dy,dz,e) = u;
|
||||
y(c,1,dx,dy,dz,e) = v;
|
||||
y(c,2,dx,dy,dz,e) = w;
|
||||
}
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES)
|
||||
{
|
||||
y(dx,dy,dz,c,0,e) = u;
|
||||
y(dx,dy,dz,c,1,e) = v;
|
||||
y(dx,dy,dz,c,2,e) = w;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace quadrature_interpolator
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
int Q1D, int NBZ>
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
|
||||
int VDIM, int D1D, int Q1D, int NBZ>
|
||||
QuadratureInterpolator::GradKernelType
|
||||
QuadratureInterpolator::GradKernels::Kernel()
|
||||
{
|
||||
@@ -598,17 +389,6 @@ QuadratureInterpolator::GradKernels::Kernel()
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
int NBZ>
|
||||
QuadratureInterpolator::CollocatedGradKernelType
|
||||
QuadratureInterpolator::CollocatedGradKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -23,73 +23,50 @@ template <bool P>
|
||||
void InitGradByNodesKernels()
|
||||
{
|
||||
using k = QuadratureInterpolator::GradKernels;
|
||||
constexpr auto L = QVectorLayout::byNODES;
|
||||
// 2D
|
||||
k::Specialization<2,L,P,1,3,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,3,4>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,4,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,4,4>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,3,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,3,4>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,4,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,4,4>::template Opt<16>::Add();
|
||||
|
||||
k::Specialization<2,L,P,2,2,2>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,2,2,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,2,2,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,2,5>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,2,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,2>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,5>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,6>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,L,P,2,3,3>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,3,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,4,3>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,3,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,3,3>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,3,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,3>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,3,6>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,L,P,2,4,4>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,5>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,7>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,4>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,5>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,7>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,L,P,2,5,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,5,6>::template Opt<2>::Add();
|
||||
// 3D
|
||||
k::Specialization<3,L,P,1,2,4>::Add();
|
||||
k::Specialization<3,L,P,1,3,3>::Add();
|
||||
k::Specialization<3,L,P,1,3,4>::Add();
|
||||
k::Specialization<3,L,P,1,3,6>::Add();
|
||||
k::Specialization<3,L,P,1,4,4>::Add();
|
||||
k::Specialization<3,L,P,1,4,8>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,2,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,3,3>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,3,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,4,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,4,8>::template Opt<1>::Add();
|
||||
|
||||
k::Specialization<3,L,P,3,2,3>::Add();
|
||||
k::Specialization<3,L,P,3,2,4>::Add();
|
||||
k::Specialization<3,L,P,3,2,5>::Add();
|
||||
k::Specialization<3,L,P,3,2,6>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,3>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,5>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,6>::template Opt<1>::Add();
|
||||
|
||||
k::Specialization<3,L,P,3,3,3>::Add();
|
||||
k::Specialization<3,L,P,3,3,4>::Add();
|
||||
k::Specialization<3,L,P,3,3,5>::Add();
|
||||
k::Specialization<3,L,P,3,3,6>::Add();
|
||||
k::Specialization<3,L,P,3,4,4>::Add();
|
||||
k::Specialization<3,L,P,3,4,6>::Add();
|
||||
k::Specialization<3,L,P,3,4,7>::Add();
|
||||
k::Specialization<3,L,P,3,4,8>::Add();
|
||||
|
||||
using k2 = QuadratureInterpolator::CollocatedGradKernels;
|
||||
|
||||
// 2D
|
||||
k2::Specialization<2,L,P,1,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,3>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,4>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,2,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,2,3>::template Opt<4>::Add();
|
||||
k2::Specialization<2,L,P,2,4>::template Opt<2>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,1,2>::Add();
|
||||
k2::Specialization<3,L,P,1,3>::Add();
|
||||
k2::Specialization<3,L,P,1,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,2,2>::Add();
|
||||
k2::Specialization<3,L,P,2,3>::Add();
|
||||
k2::Specialization<3,L,P,2,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,3,2>::Add();
|
||||
k2::Specialization<3,L,P,3,3>::Add();
|
||||
k2::Specialization<3,L,P,3,4>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,3>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,5>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,7>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,8>::template Opt<1>::Add();
|
||||
}
|
||||
|
||||
template void InitGradByNodesKernels<true>();
|
||||
|
||||
@@ -23,46 +23,22 @@ template <bool P>
|
||||
void InitGradByVDimKernels()
|
||||
{
|
||||
using k = QuadratureInterpolator::GradKernels;
|
||||
constexpr auto L = QVectorLayout::byVDIM;
|
||||
// 2D
|
||||
k::Specialization<2,L,P,1,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,1,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,1,5,8>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,1,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,1,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,1,5,8>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,L,P,2,3,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,2,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,2,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,5,8>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,3,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,5,8>::template Opt<2>::Add();
|
||||
// 3D
|
||||
k::Specialization<3,L,P,1,3,4>::Add();
|
||||
k::Specialization<3,L,P,1,4,6>::Add();
|
||||
k::Specialization<3,L,P,1,5,8>::Add();
|
||||
k::Specialization<3,L,P,3,3,4>::Add();
|
||||
k::Specialization<3,L,P,3,4,6>::Add();
|
||||
k::Specialization<3,L,P,3,5,8>::Add();
|
||||
|
||||
using k2 = QuadratureInterpolator::CollocatedGradKernels;
|
||||
// 2D
|
||||
k2::Specialization<2,L,P,1,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,3>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,4>::template Opt<16>::Add();
|
||||
|
||||
k2::Specialization<2,L,P,2,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,2,3>::template Opt<4>::Add();
|
||||
k2::Specialization<2,L,P,2,4>::template Opt<2>::Add();
|
||||
|
||||
// 3D
|
||||
k2::Specialization<3,L,P,1,2>::Add();
|
||||
k2::Specialization<3,L,P,1,3>::Add();
|
||||
k2::Specialization<3,L,P,1,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,2,2>::Add();
|
||||
k2::Specialization<3,L,P,2,3>::Add();
|
||||
k2::Specialization<3,L,P,2,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,3,2>::Add();
|
||||
k2::Specialization<3,L,P,3,3>::Add();
|
||||
k2::Specialization<3,L,P,3,4>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,1,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,1,4,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,1,5,8>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,3,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,3,4,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,3,5,8>::template Opt<1>::Add();
|
||||
}
|
||||
|
||||
template void InitGradByVDimKernels<true>();
|
||||
|
||||
@@ -600,55 +600,34 @@ void QuadratureInterpolator::Determinants(const Vector &e_vec,
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace internal::quadrature_interpolator;
|
||||
|
||||
using EvalKernel = QuadratureInterpolator::EvalKernelType;
|
||||
using TensorEvalKernel = QuadratureInterpolator::TensorEvalKernelType;
|
||||
using GradKernel = QuadratureInterpolator::GradKernelType;
|
||||
using CollocatedGradKernel = QuadratureInterpolator::CollocatedGradKernelType;
|
||||
|
||||
template <QVectorLayout Q_LAYOUT>
|
||||
TensorEvalKernel FallbackTensorEvalKernel(int DIM)
|
||||
{
|
||||
if (DIM == 1) { return Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return Values2D<Q_LAYOUT>; }
|
||||
else if (DIM == 3) { return Values3D<Q_LAYOUT>; }
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
|
||||
GradKernel GetGradKernel(int DIM)
|
||||
{
|
||||
if (DIM == 1) { return Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return Derivatives2D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 3) { return Derivatives3D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
|
||||
template<QVectorLayout Q_LAYOUT>
|
||||
GradKernel GetGradKernel(int DIM, bool GRAD_PHYS)
|
||||
{
|
||||
if (GRAD_PHYS) { return GetGradKernel<Q_LAYOUT, true>(DIM); }
|
||||
else { return GetGradKernel<Q_LAYOUT, false>(DIM); }
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
|
||||
CollocatedGradKernel GetCollocatedGradKernel(int DIM)
|
||||
{
|
||||
if (DIM == 1) { return CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 3) { return CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT>
|
||||
CollocatedGradKernel GetCollocatedGradKernel(int DIM, bool GRAD_PHYS)
|
||||
{
|
||||
if (GRAD_PHYS) { return GetCollocatedGradKernel<Q_LAYOUT, true>(DIM); }
|
||||
else { return GetCollocatedGradKernel<Q_LAYOUT, false>(DIM); }
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <int DIM, int VDIM, int ND, int NQ>
|
||||
@@ -694,13 +673,6 @@ GradKernel QuadratureInterpolator::GradKernels::Fallback(
|
||||
else { return GetGradKernel<QVectorLayout::byVDIM>(DIM, GRAD_PHYS); }
|
||||
}
|
||||
|
||||
CollocatedGradKernel QuadratureInterpolator::CollocatedGradKernels::Fallback(
|
||||
int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int, int)
|
||||
{
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES) { return GetCollocatedGradKernel<QVectorLayout::byNODES>(DIM, GRAD_PHYS); }
|
||||
else { return GetCollocatedGradKernel<QVectorLayout::byVDIM>(DIM, GRAD_PHYS); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
namespace internal
|
||||
|
||||
@@ -138,10 +138,6 @@ public:
|
||||
using GradKernelType = void(*)(const int, const real_t *, const real_t *,
|
||||
const real_t *, const real_t *, real_t *,
|
||||
const int, const int, const int, const int);
|
||||
using CollocatedGradKernelType = void(*)(const int, const real_t *,
|
||||
const real_t *, const real_t *,
|
||||
real_t *, const int, const int,
|
||||
const int);
|
||||
using DetKernelType = void(*)(const int NE, const real_t *, const real_t *,
|
||||
const real_t *, real_t *, const int, const int,
|
||||
Vector *);
|
||||
@@ -156,8 +152,6 @@ public:
|
||||
(int, QVectorLayout, bool, int, int, int), (int));
|
||||
MFEM_REGISTER_KERNELS(DetKernels, DetKernelType, (int, int, int, int));
|
||||
MFEM_REGISTER_KERNELS(EvalKernels, EvalKernelType, (int, int, int, int));
|
||||
MFEM_REGISTER_KERNELS(CollocatedGradKernels, CollocatedGradKernelType,
|
||||
(int, QVectorLayout, bool, int, int), (int));
|
||||
|
||||
static struct Kernels { Kernels(); } kernels;
|
||||
};
|
||||
|
||||
+40
-911
File diff suppressed because it is too large
Load Diff
+19
-156
@@ -40,10 +40,6 @@ protected:
|
||||
OperatorHandle fw_t_oper; ///< Forward true-dof operator
|
||||
OperatorHandle bw_t_oper; ///< Backward true-dof operator
|
||||
|
||||
bool use_ea;
|
||||
|
||||
MemoryType d_mt;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
bool parallel;
|
||||
#endif
|
||||
@@ -63,23 +59,14 @@ protected:
|
||||
|
||||
public:
|
||||
/** Construct a transfer algorithm between the domain, @a dom_fes_, and
|
||||
range, @a ran_fes_, FE spaces, d_mt_ will specify memory space for
|
||||
large data structures */
|
||||
GridTransfer(FiniteElementSpace &dom_fes_,
|
||||
FiniteElementSpace &ran_fes_);
|
||||
range, @a ran_fes_, FE spaces. */
|
||||
GridTransfer(FiniteElementSpace &dom_fes_, FiniteElementSpace &ran_fes_);
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~GridTransfer() { }
|
||||
|
||||
/** Uses device friendly element assembly versions for L2Projection
|
||||
transfers, L2, H1 FEM spaces currently supported */
|
||||
void UseEA(bool use_ea_) { use_ea = use_ea_;}
|
||||
|
||||
/** Set memory type for large data structures */
|
||||
void SetMemType(MemoryType d_mt_) {d_mt = d_mt_;}
|
||||
|
||||
/** @brief Set the desired Operator::Type for the construction of all
|
||||
operators defined by the underlying transfer algorithm. */
|
||||
operators defined by the underlying transfer algorithm. */
|
||||
/** The default value is Operator::ANY_TYPE which typically corresponds to a
|
||||
matrix-free operator representation. Note that derived classes are not
|
||||
required to support this setting and can ignore it. */
|
||||
@@ -182,8 +169,7 @@ public:
|
||||
smaller than the number of coarse dofs. */
|
||||
class L2ProjectionGridTransfer : public GridTransfer
|
||||
{
|
||||
// Must be public due to host device lambdas
|
||||
public:
|
||||
protected:
|
||||
/** Abstract class representing projection operator between a high-order
|
||||
finite element space on a coarse mesh, and a low-order finite element
|
||||
space on a refined mesh (LOR). We assume that the low-order space,
|
||||
@@ -208,13 +194,10 @@ public:
|
||||
const FiniteElementSpace& fes_ho;
|
||||
const FiniteElementSpace& fes_lor;
|
||||
|
||||
MemoryType d_mt;
|
||||
Array<int> offsets;
|
||||
Table ho2lor;
|
||||
|
||||
L2Projection(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
const FiniteElementSpace& fes_lor_);
|
||||
|
||||
void BuildHo2Lor(int nel_ho, int nel_lor,
|
||||
const CoarseFineTransformations& cf_tr);
|
||||
@@ -224,50 +207,6 @@ public:
|
||||
ElementTransformation* tr_lor,
|
||||
IntegrationPointTransformation& ip_tr,
|
||||
DenseMatrix& M_mixed_el) const;
|
||||
|
||||
void ElemMixedMass(Geometry::Type geom, const FiniteElement& fe_ho,
|
||||
const FiniteElement& fe_lor,
|
||||
ElementTransformation* el_tr,
|
||||
IntegrationPointTransformation& ip_tr,
|
||||
DenseMatrix& B_L, DenseMatrix& B_H) const;
|
||||
public:
|
||||
/* Returns the Mixed Mass M_LH via device element assembly by building the
|
||||
basis functions and data at the quadrature points. */
|
||||
void MixedMassEA(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_,
|
||||
Vector &M_LH,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
};
|
||||
|
||||
// Class below must be public as we now have device code
|
||||
public:
|
||||
class H1SpaceMixedMassOperator : public Operator
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace* fes_ho;
|
||||
const FiniteElementSpace* fes_lor;
|
||||
Table* ho2lor;
|
||||
Vector* M_LH_ea;
|
||||
public:
|
||||
H1SpaceMixedMassOperator(const FiniteElementSpace* fes_ho_,
|
||||
const FiniteElementSpace* fes_lor_,
|
||||
Table* ho2lor_, Vector* M_LH_ea_);
|
||||
void Mult(const Vector& x, Vector& y) const;
|
||||
void MultTranspose(const Vector& x, Vector& y) const;
|
||||
};
|
||||
|
||||
class H1SpaceLumpedMassOperator : public Operator
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace* fes_ho;
|
||||
const FiniteElementSpace* fes_lor;
|
||||
Vector* ML_inv; // inverse of lumped M_L
|
||||
public:
|
||||
H1SpaceLumpedMassOperator(const FiniteElementSpace* fes_ho_,
|
||||
const FiniteElementSpace* fes_lor_,
|
||||
Vector& ML_inv_);
|
||||
void Mult(const Vector& x, Vector& y) const;
|
||||
void MultTranspose(const Vector& x, Vector& y) const;
|
||||
};
|
||||
|
||||
/** Class for projection operator between a L2 high-order finite element
|
||||
@@ -275,24 +214,17 @@ public:
|
||||
refined mesh (LOR). */
|
||||
class L2ProjectionL2Space : public L2Projection
|
||||
{
|
||||
/// The restriction and prolongation operators are represented as dense
|
||||
/// elementwise matrices (of potentially different sizes, because of mixed
|
||||
/// meshes or p-refinement). The matrix entries are stored in the R and P
|
||||
/// arrays. The entries of the i'th high-order element are stored at the
|
||||
/// index given by offsets[i].
|
||||
// The restriction and prolongation operators are represented as dense
|
||||
// elementwise matrices (of potentially different sizes, because of mixed
|
||||
// meshes or p-refinement). The matrix entries are stored in the R and P
|
||||
// arrays. The entries of the i'th high-order element are stored at the
|
||||
// index given by offsets[i].
|
||||
mutable Array<real_t> R, P;
|
||||
|
||||
const bool use_ea;
|
||||
Array<int> offsets;
|
||||
|
||||
public:
|
||||
L2ProjectionL2Space(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_,
|
||||
const bool use_ea_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
|
||||
/*Same as above but assembles and stores R_ea, P_ea */
|
||||
void EAL2ProjectionL2Space();
|
||||
|
||||
const FiniteElementSpace& fes_lor_);
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a coarse mesh
|
||||
/// with a higher order L2 finite element space, to <tt>y</tt>, primal
|
||||
/// field coefficients defined on a refined mesh with a low order L2
|
||||
@@ -300,10 +232,6 @@ public:
|
||||
/// the coarse mesh. Coefficients are computed through minimization of L2
|
||||
/// error between the fields.
|
||||
void Mult(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Perform mult on the device (same as above)
|
||||
void EAMult(const Vector& x, Vector& y) const;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a refined mesh
|
||||
/// with a low order L2 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a coarse mesh with a higher order L2 finite
|
||||
@@ -312,9 +240,6 @@ public:
|
||||
/// error between the primal fields. Note, if the <tt>x</tt>-coefficients
|
||||
/// come from ProlongateTranspose, then mass is conserved.
|
||||
void MultTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
void EAMultTranspose(const Vector& x, Vector& y) const;
|
||||
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a refined mesh
|
||||
/// with a low order L2 finite element space, to <tt>y</tt>, primal field
|
||||
/// coefficients defined on a coarse mesh with a higher order L2 finite
|
||||
@@ -323,9 +248,6 @@ public:
|
||||
/// left-inverse prolongation operation. This functionality is also
|
||||
/// provided as an Operator by L2Prolongation.
|
||||
void Prolongate(const Vector& x, Vector& y) const override;
|
||||
|
||||
void EAProlongate(const Vector& x, Vector& y) const;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a coarse mesh with
|
||||
/// a higher order L2 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a refined mesh with a low order L2 finite
|
||||
@@ -334,46 +256,21 @@ public:
|
||||
/// conservative left-inverse prolongation operation. This functionality
|
||||
/// is also provided as an Operator by L2Prolongation.
|
||||
void ProlongateTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
void EAProlongateTranspose(const Vector& x, Vector& y) const;
|
||||
|
||||
void SetRelTol(real_t p_rtol_) override { } ///< No-op.
|
||||
void SetAbsTol(real_t p_atol_) override { } ///< No-op.
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/// Class below must be public as we now have device code
|
||||
public:
|
||||
|
||||
/** Projection operator between a H1 high-order finite element space on a
|
||||
coarse mesh, and a H1 low-order finite element space on a refined mesh
|
||||
(LOR). */
|
||||
class L2ProjectionH1Space : public L2Projection
|
||||
{
|
||||
const bool use_ea;
|
||||
|
||||
public:
|
||||
L2ProjectionH1Space(const FiniteElementSpace &fes_ho_,
|
||||
const FiniteElementSpace &fes_lor_,
|
||||
const bool use_ea_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
const FiniteElementSpace &fes_lor_);
|
||||
#ifdef MFEM_USE_MPI
|
||||
L2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
|
||||
const ParFiniteElementSpace &pfes_lor_,
|
||||
const bool use_ea_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
#endif
|
||||
/// Same as above but assembles action of R through 4 parts:
|
||||
/// ( ) inv( lumped(M_L) ), which is a diagonal matrix (essentially a vector)
|
||||
/// ( ) ElementRestrictionOperator for LOR space
|
||||
/// ( ) mixed mass matrix M_{LH}
|
||||
/// ( ) ElementRestrictionOperator for HO space
|
||||
void EAL2ProjectionH1Space();
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void EAL2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
|
||||
const ParFiniteElementSpace &pfes_lor_);
|
||||
const ParFiniteElementSpace &pfes_lor_);
|
||||
#endif
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a coarse mesh
|
||||
/// with a higher order H1 finite element space, to <tt>y</tt>, primal
|
||||
@@ -382,7 +279,6 @@ public:
|
||||
/// the coarse mesh. Coefficients are computed through minimization of L2
|
||||
/// error between the fields.
|
||||
void Mult(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a refined mesh
|
||||
/// with a low order H1 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a coarse mesh with a higher order H1 finite
|
||||
@@ -391,7 +287,6 @@ public:
|
||||
/// error between the primal fields. Note, if the <tt>x</tt>-coefficients
|
||||
/// come from ProlongateTranspose, then mass is conserved.
|
||||
void MultTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a refined mesh
|
||||
/// with a low order H1 finite element space, to <tt>y</tt>, primal field
|
||||
/// coefficients defined on a coarse mesh with a higher order H1 finite
|
||||
@@ -400,7 +295,6 @@ public:
|
||||
/// left-inverse prolongation operation. This functionality is also
|
||||
/// provided as an Operator by L2Prolongation.
|
||||
void Prolongate(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a coarse mesh with
|
||||
/// a higher order H1 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a refined mesh with a low order H1 finite
|
||||
@@ -409,22 +303,14 @@ public:
|
||||
/// conservative left-inverse prolongation operation. This functionality
|
||||
/// is also provided as an Operator by L2Prolongation.
|
||||
void ProlongateTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Returns the inverse of an on-rank lumped mass matrix
|
||||
void LumpedMassInverse(Vector& ML_inv) const;
|
||||
|
||||
void SetRelTol(real_t p_rtol_) override;
|
||||
void SetAbsTol(real_t p_atol_) override;
|
||||
|
||||
protected:
|
||||
/// Sets up the PCG solver (sets parameters, operator, and preconditioner)
|
||||
void SetupPCG();
|
||||
|
||||
/// @brief Computes on-rank R and M_LH matrices. If true, computes mixed mass and/or
|
||||
/// inverse lumped mass matrix error when compared to device implementation.
|
||||
/// Computes on-rank R and M_LH matrices.
|
||||
std::pair<std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>> ComputeSparseRAndM_LH();
|
||||
|
||||
/// @brief Recovers vector of tdofs given a vector of dofs and a finite
|
||||
/// element space
|
||||
void GetTDofs(const FiniteElementSpace& fes, const Vector& x, Vector& X) const;
|
||||
@@ -447,8 +333,10 @@ public:
|
||||
void TDofsListByVDim(const FiniteElementSpace& fes,
|
||||
int vdim,
|
||||
Array<int>& vdofs_list) const;
|
||||
|
||||
/// Returns the inverse of an on-rank lumped mass matrix
|
||||
void LumpedMassInverse(Vector& ML_inv) const;
|
||||
/// @brief Computes sparsity pattern and initializes R matrix.
|
||||
///
|
||||
/// Based on BilinearForm::AllocMat(), except maps between coarse HO
|
||||
/// elements and refined LOR elements.
|
||||
std::unique_ptr<SparseMatrix> AllocR();
|
||||
@@ -458,34 +346,10 @@ public:
|
||||
// The restriction operator is represented as an Operator R. The
|
||||
// prolongation operator is a dense matrix computed as the inverse of (R^T
|
||||
// M_L R), and hence, is not stored.
|
||||
// If element assembly is enabled
|
||||
std::unique_ptr<Operator> R;
|
||||
// Used to compute P = (RT*M_LH)^(-1) M_LH^T
|
||||
std::unique_ptr<Operator> M_LH;
|
||||
// Inverted operator in P = (RT*M_LH)^(-1) M_LH^T. Used to compute P via PCG.
|
||||
std::unique_ptr<Operator> RTxM_LH;
|
||||
// Lumped M_L inverse operator built via EA. Wrapped with restriction maps
|
||||
// to multiply with scalar TDof LOR vectors.
|
||||
std::unique_ptr<Operator> ML_inv_vea;
|
||||
// LDof Mixed mass operator built via EA. Wrapped with restrition maps to send
|
||||
// scalar LDof HO vectors to LDof LOR vectors.
|
||||
Operator *M_LH_local_op;
|
||||
|
||||
// Scalar finite element spaces for stored Tdof-to-and-from-LDof maps.
|
||||
FiniteElementSpace* fes_ho_scalar;
|
||||
FiniteElementSpace* fes_lor_scalar;
|
||||
// Element Assembled mixed mass
|
||||
Vector M_LH_ea;
|
||||
// Element Assembled lumped M_L inverse built via EA. Stores diagonal as a Ldof vector.
|
||||
Vector ML_inv_ea;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParFiniteElementSpace* pfes_ho_scalar;
|
||||
ParFiniteElementSpace* pfes_lor_scalar;
|
||||
Vector RML_inv;
|
||||
#endif
|
||||
|
||||
friend class L2ProjectionL2Space;
|
||||
};
|
||||
|
||||
/** Mass-conservative prolongation operator going in the opposite direction
|
||||
@@ -515,8 +379,7 @@ public:
|
||||
public:
|
||||
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes_,
|
||||
FiniteElementSpace &fine_fes_,
|
||||
bool force_l2_space_ = false,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType()) //move to method
|
||||
bool force_l2_space_ = false)
|
||||
: GridTransfer(coarse_fes_, fine_fes_),
|
||||
F(NULL), B(NULL), force_l2_space(force_l2_space_)
|
||||
{ }
|
||||
|
||||
@@ -47,8 +47,6 @@ list(APPEND HDRS
|
||||
zstr.hpp
|
||||
hash.hpp
|
||||
isockstream.hpp
|
||||
mdarray.hpp
|
||||
mdspan.hpp
|
||||
kdtree.hpp
|
||||
mem_alloc.hpp
|
||||
mem_manager.hpp
|
||||
|
||||
+5
-69
@@ -45,8 +45,6 @@ template <class T>
|
||||
class Array
|
||||
{
|
||||
protected:
|
||||
template<typename mfem_type, int N, typename L> friend class MDSpan;
|
||||
|
||||
/// Pointer to data
|
||||
Memory<T> data;
|
||||
/// Size of the array
|
||||
@@ -54,7 +52,10 @@ protected:
|
||||
|
||||
inline void GrowSize(int minsize);
|
||||
|
||||
static_assert(std::is_trivial<T>::value, "type T must be trivial");
|
||||
static inline void TypeAssert()
|
||||
{
|
||||
static_assert(std::is_trivial<T>::value, "type T must be trivial");
|
||||
}
|
||||
|
||||
public:
|
||||
friend void Swap<T>(Array<T> &, Array<T> &);
|
||||
@@ -94,26 +95,11 @@ public:
|
||||
template <typename CT, int N>
|
||||
explicit inline Array(const CT (&values)[N]);
|
||||
|
||||
/**
|
||||
* @brief Construct a new Array object from an initializer list.
|
||||
*
|
||||
* @param init_list List of entities to construct from.
|
||||
*/
|
||||
Array(const std::initializer_list<T> &init_list)
|
||||
: Array(static_cast<int>(init_list.size()))
|
||||
{
|
||||
auto * it = GetData();
|
||||
for (auto value : init_list)
|
||||
{
|
||||
*it++ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Move constructor ("steals" data from 'src')
|
||||
inline Array(Array<T> &&src) { Swap(src, *this); }
|
||||
|
||||
/// Destructor
|
||||
inline ~Array() { data.Delete(); }
|
||||
inline ~Array() { TypeAssert(); data.Delete(); }
|
||||
|
||||
/// Assignment operator: deep copy from 'src'.
|
||||
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
|
||||
@@ -218,8 +204,6 @@ public:
|
||||
/// Delete the whole array.
|
||||
inline void DeleteAll();
|
||||
|
||||
/// Reduces the capacity of the array to exactly match the current size.
|
||||
inline void ShrinkToFit();
|
||||
|
||||
/// Create a copy of the internal array to the provided @a copy.
|
||||
inline void Copy(Array ©) const;
|
||||
@@ -237,18 +221,6 @@ public:
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
|
||||
/**
|
||||
* @brief Permute the array using the provided indices. Sorts the indices
|
||||
* variable in the process, thereby destroying the permutation. The rvalue
|
||||
* reference is to be used when this destruction is allowed, whilst the const
|
||||
* reference preserves at the cost of duplication.
|
||||
*
|
||||
* @param indices The indices of the ordering. data[i] = data[indices[i]].
|
||||
*/
|
||||
template <typename I>
|
||||
inline void Permute(I &&indices);
|
||||
template <typename I>
|
||||
inline void Permute(const I &indices) { Permute(I(indices)); }
|
||||
|
||||
/// Copy sub array starting from @a offset out to the provided @a sa.
|
||||
inline void GetSubArray(int offset, int sa_size, Array<T> &sa) const;
|
||||
@@ -303,9 +275,6 @@ public:
|
||||
/// Return 1 if the array is sorted from lowest to highest. Otherwise return 0.
|
||||
int IsSorted() const;
|
||||
|
||||
/// Does the Array have Size zero.
|
||||
bool IsEmpty() const { return Size() == 0; }
|
||||
|
||||
/// Fill the entries of the array with the cumulative sum of the entries.
|
||||
void PartialSum();
|
||||
|
||||
@@ -523,8 +492,6 @@ public:
|
||||
BlockArray(int block_size = 16*1024);
|
||||
BlockArray(const BlockArray<T> &other); // deep copy
|
||||
BlockArray& operator=(const BlockArray&) = delete; // not supported
|
||||
BlockArray(BlockArray<T> &&other) = default;
|
||||
BlockArray& operator=(BlockArray<T> &&other) = default;
|
||||
~BlockArray() { Destroy(); }
|
||||
|
||||
/// Allocate and construct a new item in the array, return its index.
|
||||
@@ -646,8 +613,6 @@ public:
|
||||
|
||||
iterator begin() { return size ? iterator(this) : iterator(true); }
|
||||
iterator end() { return iterator(); }
|
||||
const_iterator begin() const { return cbegin(); }
|
||||
const_iterator end() const { return cend(); }
|
||||
|
||||
const_iterator cbegin() const
|
||||
{ return size ? const_iterator(this) : const_iterator(true); }
|
||||
@@ -720,35 +685,6 @@ inline void Array<T>::GrowSize(int minsize)
|
||||
data = p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Array<T>::ShrinkToFit()
|
||||
{
|
||||
if (Capacity() == size) { return; }
|
||||
Memory<T> p(size, data.GetMemoryType());
|
||||
p.CopyFrom(data, size);
|
||||
p.UseDevice(data.UseDevice());
|
||||
data.Delete();
|
||||
data = p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename I>
|
||||
inline void Array<T>::Permute(I &&indices)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
auto current = i;
|
||||
while (i != indices[current])
|
||||
{
|
||||
auto next = indices[current];
|
||||
std::swap(data[current], data[next]);
|
||||
indices[current] = current;
|
||||
current = next;
|
||||
}
|
||||
indices[current] = current;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> template <typename CT>
|
||||
inline Array<T> &Array<T>::operator=(const Array<CT> &src)
|
||||
{
|
||||
|
||||
@@ -838,16 +838,6 @@ inline void hypre_forall(int N, lambda &&body)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the most general MemoryClass that can be used with mfem::hypre_forall
|
||||
// kernels. The returned MemoryClass is the same as the one returned by
|
||||
// GerHypreMemoryClass() except when hypre is configured to use UVM, in which
|
||||
// case this function returns MemoryClass::HOST or MemoryClass::DEVICE depending
|
||||
// on the result of HypreUsingGPU().
|
||||
inline MemoryClass GetHypreForallMemoryClass()
|
||||
{
|
||||
return HypreUsingGPU() ? MemoryClass::DEVICE : MemoryClass::HOST;
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (c) 2010-2023, 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_MDARRAY
|
||||
#define MFEM_MDARRAY
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#include "array.hpp"
|
||||
#include "mdspan.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<typename T, int N, typename Layout = MDLayoutLeft<N>>
|
||||
struct MDArray : public MDSpan<Array<T>, N, Layout>
|
||||
{
|
||||
using base_t = MDSpan<Array<T>, N, Layout>;
|
||||
|
||||
/**
|
||||
* @brief MDArray default constructor (recursion)
|
||||
*/
|
||||
MDArray(): base_t() { }
|
||||
|
||||
/**
|
||||
* @brief MDArray recursion constructor
|
||||
* @param[in] n Dimension indice
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDArray(int n, Ts... args): MDArray(args...) { base_t::Setup(n, args...); }
|
||||
|
||||
/// Move constructor not supported
|
||||
MDArray(MDArray&&) = delete;
|
||||
|
||||
/// Copy constructor not supported
|
||||
MDArray(const MDArray&) = delete;
|
||||
|
||||
/// Move assignment not supported
|
||||
MDArray& operator=(MDArray&&) = delete;
|
||||
|
||||
/// Copy assignment not supported
|
||||
MDArray& operator=(const MDArray&) = delete;
|
||||
|
||||
using Array<T>::Read;
|
||||
using Array<T>::Write;
|
||||
using Array<T>::ReadWrite;
|
||||
using Array<T>::HostRead;
|
||||
using Array<T>::HostWrite;
|
||||
using Array<T>::HostReadWrite;
|
||||
|
||||
using Array<T>::Assign;
|
||||
using Array<T>::Print;
|
||||
|
||||
using Array<T>::GetData;
|
||||
|
||||
using Array<T>::operator=;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDARRAY
|
||||
@@ -1,417 +0,0 @@
|
||||
// Copyright (c) 2010-2023, 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_MDSPAN_HPP
|
||||
#define MFEM_MDSPAN_HPP
|
||||
|
||||
#include <list>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "backends.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal // experimental helper functions for mfem::MDLayout
|
||||
{
|
||||
|
||||
// md_sequence represents a compile-time sequence of integers
|
||||
template <typename T, T... args> struct md_sequence { };
|
||||
|
||||
template <typename T, int N, bool left> struct make_md_sequence;
|
||||
|
||||
// make_sequence, specialized for left (default) and right layout
|
||||
template <typename T, int N, bool left = true>
|
||||
using make_sequence = typename make_md_sequence<T, N, left>::type;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// @brief The MDOffset class computes the multi-dimensional offsets
|
||||
template <int n, int N, typename T, typename... Ts>
|
||||
struct MDOffset
|
||||
{
|
||||
static MFEM_HOST_DEVICE inline
|
||||
T offset(const int (&Sd)[N], T nd, Ts... args)
|
||||
{ return nd * Sd[n-1] + MDOffset<n+1, N, Ts...>::offset(Sd, args...); }
|
||||
};
|
||||
|
||||
template <int N, typename T, typename... Ts>
|
||||
struct MDOffset<N, N, T, Ts...>
|
||||
{
|
||||
static MFEM_HOST_DEVICE inline
|
||||
T offset(const int (&Sd)[N], T nd) { return nd * Sd[N-1]; }
|
||||
};
|
||||
|
||||
/// @brief The MDTensor class holds the pointer and strides for each dimension
|
||||
template<int N, typename T> class MDTensor
|
||||
{
|
||||
T *ptr;
|
||||
int Sd[N];
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
MDTensor() = delete;
|
||||
|
||||
/// Copy constructor (default)
|
||||
MDTensor(const MDTensor&) = default;
|
||||
|
||||
/// Copy assignment (default)
|
||||
MDTensor& operator=(const MDTensor&) = default;
|
||||
|
||||
/// Constructor to initialize a tensor from a pointer and strides
|
||||
template <typename... Args> MFEM_HOST_DEVICE
|
||||
MDTensor(T *ptr, const int (&sd)[N]): ptr(ptr)
|
||||
{ for (int i = 0; i < N; ++i) { Sd[i] = sd[i]; } }
|
||||
|
||||
/// Accessor for the data
|
||||
template <typename... Ts> MFEM_HOST_DEVICE inline
|
||||
T& operator()(Ts... args) { return ptr[Offset(args...)]; }
|
||||
|
||||
/// Const accessor for the data
|
||||
template <typename... Ts> MFEM_HOST_DEVICE inline
|
||||
T& operator()(Ts... args) const { return ptr[Offset(args...)]; }
|
||||
|
||||
/// Offset computation
|
||||
template <typename... Ts> MFEM_HOST_DEVICE inline
|
||||
int Offset(Ts... args) const
|
||||
{
|
||||
static_assert(sizeof...(args) == N, "Wrong number of dimensions");
|
||||
return MDOffset<1, N, Ts...>::offset(Sd, args...);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief The MDLayout class, defaulted to a column-major (left) ordering
|
||||
template<int N, bool left = true> struct MDLayout
|
||||
{
|
||||
/// Create a layout with the internal::md_sequence
|
||||
template <int... args>
|
||||
static constexpr auto Make(internal::md_sequence<int, args...>)
|
||||
-> std::array<int, sizeof...(args)> { return {(static_cast<int>(args))...}; }
|
||||
|
||||
/// Array holding the layout permutation
|
||||
using perm_type = std::array<int, N>;
|
||||
perm_type perm = Make(internal::make_sequence<int, N, left> {});
|
||||
|
||||
/// Default constructor
|
||||
MDLayout() = default;
|
||||
|
||||
/// Copy constructor (default)
|
||||
MDLayout(const MDLayout&) = default;
|
||||
|
||||
/// Copy assignment (default)
|
||||
MDLayout& operator=(const MDLayout&) = default;
|
||||
|
||||
/// Constructor to initialize a layout from an array of indices
|
||||
template <typename... Ts>
|
||||
MDLayout(int n, Ts... args) noexcept: MDLayout(args...)
|
||||
{
|
||||
constexpr int k = N - sizeof...(args) - 1;
|
||||
static_assert(0 <= k && k < N, "Index out of bounds!");
|
||||
perm[k] = n;
|
||||
}
|
||||
|
||||
/// Access layout entries using operator()
|
||||
inline int operator()(int i) const
|
||||
{ return Assert(i), perm[static_cast<typename perm_type::size_type>(i)]; }
|
||||
|
||||
/// Access layout entries using operator[]
|
||||
inline int operator[](int i) const
|
||||
{ return Assert(i), perm[static_cast<typename perm_type::size_type>(i)]; }
|
||||
|
||||
/// Asserts the given index is valid (only in MFEM_DEBUG)
|
||||
inline void Assert(const int k) const
|
||||
{
|
||||
MFEM_CONTRACT_VAR(k);
|
||||
MFEM_ASSERT(0 <= k && k < N, "Index should be in [0," << (N-1) << "]");
|
||||
}
|
||||
};
|
||||
|
||||
/// Left (Column-major (Fortran)) and Right (Row-major (C/C++)) layouts
|
||||
template<int N> using MDLayoutLeft = MDLayout<N, true>;
|
||||
template<int N> using MDLayoutRight = MDLayout<N, false>;
|
||||
|
||||
/// \brief The MDSpan base class is a generic non-owning mfem_type's view
|
||||
/// that reinterprets it as a multidimensional type.
|
||||
template<typename mfem_type, int N, class layout_type = MDLayoutLeft<N>>
|
||||
class MDSpan : protected mfem_type
|
||||
{
|
||||
protected:
|
||||
using T = typename std::remove_pointer<decltype(mfem_type::data.h_ptr)>::type;
|
||||
|
||||
int Nd[N], Sd[N]; // dimension sizes and strides, once the layout is set
|
||||
layout_type layout; // stored layout, useful for reshapes
|
||||
|
||||
/// Set the dimensions (Nd) and strides (Sd) during contruction.
|
||||
/// When all the arguments have been processed, SetSize is called on the
|
||||
/// mfem_type with Device::GetMemoryType() as memory type and SetLayout is
|
||||
/// called using the layout.
|
||||
template <typename... Ts> void Setup(int dim, Ts... args)
|
||||
{
|
||||
constexpr int k = N - sizeof...(args) - 1;
|
||||
Sd[k] = Nd[k] = dim;
|
||||
if (k > 0) { return; }
|
||||
int psize = 1;
|
||||
for (int i = 0; i < N; i++) { psize *= Nd[i]; }
|
||||
mfem_type::SetSize(static_cast<int>(psize), Device::GetMemoryType());
|
||||
SetLayout(layout);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor (recursion)
|
||||
MDSpan() noexcept: mfem_type() { }
|
||||
|
||||
/// Recursion constructor
|
||||
template <typename... Ts>
|
||||
MDSpan(int n, Ts... args): MDSpan(args...) { Setup(n, args...); }
|
||||
|
||||
/// Move constructor (delete)
|
||||
MDSpan(MDSpan&&) = delete;
|
||||
|
||||
/// Copy constructor (delete)
|
||||
MDSpan(const MDSpan&) = delete;
|
||||
|
||||
/// Move assignment (delete)
|
||||
MDSpan& operator=(MDSpan&&) = delete;
|
||||
|
||||
/// Copy assignment (delete)
|
||||
MDSpan& operator=(const MDSpan&) = delete;
|
||||
|
||||
/// Return the ith dimension
|
||||
int Extent(int i) const { return Nd[i]; }
|
||||
|
||||
/// Return the size of the span.
|
||||
int Size() const { return mfem_type::Size(); }
|
||||
|
||||
/// Store and use the given layout to update the strides
|
||||
template<typename Layout> void SetLayout(const Layout &l)
|
||||
{
|
||||
layout = l;
|
||||
Sd[l[0]] = 1;
|
||||
for (int i = 1; i < N; i++) { Sd[l[i]] = Nd[l[i-1]] * Sd[l[i-1]]; }
|
||||
}
|
||||
|
||||
/// Variadic resize the mfem_type
|
||||
template <typename... Ts> inline void SetSize(int size, Ts... args)
|
||||
{
|
||||
constexpr int k = N - sizeof...(args) - 1;
|
||||
Sd[k] = Nd[k] = size;
|
||||
const int msize = mfem_type::Size();
|
||||
MFEM_VERIFY(size > 0, "Size should be positive!");
|
||||
mfem_type::SetSize(msize > 0 ? msize*size : size, Device::GetMemoryType());
|
||||
MDSpan::SetSize(args...);
|
||||
}
|
||||
|
||||
/// Variadic terminal case of the mfem_type resize
|
||||
inline void SetSize(int size)
|
||||
{
|
||||
Sd[N-1] = Nd[N-1] = size;
|
||||
const int msize = mfem_type::Size();
|
||||
MFEM_VERIFY(size > 0, "Size should be positive!");
|
||||
mfem_type::SetSize(msize > 0 ? msize*size : size, Device::GetMemoryType());
|
||||
SetLayout(layout);
|
||||
}
|
||||
|
||||
/// Access mfem_type data entries using operator()
|
||||
template <typename... Ts> inline
|
||||
T& operator()(Ts... args) { return mfem_type::data[Offset(args...)]; }
|
||||
|
||||
/// Const access mfem_type data entries using operator()
|
||||
template <typename... Ts> inline const T& operator()(Ts... args) const
|
||||
{
|
||||
return mfem_type::data[Offset(args...)];
|
||||
}
|
||||
|
||||
/// Offset computation
|
||||
template <typename... Ts> inline int Offset(Ts... args) const
|
||||
{
|
||||
static_assert(sizeof...(args) == N, "Wrong number of dimensions");
|
||||
return MDOffset<1,N,Ts...>::offset(Sd, args...);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Read(mfem_type::data, mfem_type::size, on_dev)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
const MDTensor<N,const T> MDRead(bool on_dev = true) const
|
||||
{
|
||||
const T *ptr = mfem::Read(mfem_type::data, mfem_type::size, on_dev);
|
||||
return MDTensor<N,const T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Read(mfem_type::data, mfem_type::size, false)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
const MDTensor<N,const T> MDHostRead() const
|
||||
{
|
||||
const T *ptr = mfem::Read(mfem_type::data, mfem_type::size, false);
|
||||
return MDTensor<N,const T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Write(mfem_type::data, mfem_type::size, on_dev)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDWrite(bool on_dev = true)
|
||||
{
|
||||
T *ptr = mfem::Write(mfem_type::data, mfem_type::size, on_dev);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Write(mfem_type::data, mfem_type::size, false)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDHostWrite()
|
||||
{
|
||||
T *ptr = mfem::Write(mfem_type::data, mfem_type::size, false);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(mfem_type::data, mfem_type::size, on_dev)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDReadWrite(bool on_dev = true)
|
||||
{
|
||||
T *ptr = mfem::ReadWrite(mfem_type::data, mfem_type::size, on_dev);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(mfem_type::data, mfem_type::size, false)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDHostReadWrite()
|
||||
{
|
||||
T *ptr = mfem::ReadWrite(mfem_type::data, mfem_type::size, false);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// The MDReshape function allows to reshape the multi-dimentional view
|
||||
/// into a new multi-dimentional one, by the use of std::array blocks.
|
||||
/// For example, if 'this' has three dimensions {N1, N2, N3}, it could handle
|
||||
/// this->MDReshape<4>(ptr, N1, std::array<int,2> {2, N2/2}, N3);
|
||||
|
||||
// Parameter R could be omitted with c++14 standard's deduced return types
|
||||
|
||||
// first method with given data pointer and rest of arguments
|
||||
template <int R, int m = 0, int M = 0, typename... Ts>
|
||||
inline auto MDReshape(T *ptr, Ts&&... args) -> MDTensor<R,T>
|
||||
{
|
||||
rNd.clear();
|
||||
reshape_ptr = ptr;
|
||||
reshape_offset = 1, reshape_shifts[0] = reshape_shifts[1] = 0;
|
||||
return MDReshape<R,m,M>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
// variadic method, where a new block of reshape is given in argument
|
||||
template <int R, int m = 0, int M = 0, size_t P, typename... Ts>
|
||||
inline auto MDReshape(std::array<int,P> list, Ts&&... args) -> MDTensor<R,T>
|
||||
{
|
||||
reshape_shifts[0] = layout.perm[m]; // store layout shift begin
|
||||
int shifted_layout = reshape_shifts[1] + layout.perm[m];
|
||||
for (int dim: list)
|
||||
{
|
||||
rNd.push_back(dim);
|
||||
rLt[m].push_back(sub_layout_pair{shifted_layout,-1});
|
||||
shifted_layout += 1; // default left layout
|
||||
}
|
||||
reshape_shifts[1] += P-1; // update end
|
||||
return MDReshape<R,m+1,M+P>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
// variadic method, where a new dimension of reshape is given
|
||||
template <int R, int m = 0, int M = 0, typename... Ts>
|
||||
inline auto MDReshape(int dim, Ts&&... args) -> MDTensor<R,T>
|
||||
{
|
||||
rNd.push_back(dim);
|
||||
const int shift =
|
||||
reshape_shifts[0] < layout.perm[m] ? reshape_shifts[1] : 0;
|
||||
rLt[m].push_back(sub_layout_pair{layout.perm[m] + shift,-1});
|
||||
return MDReshape<R,m+1,M+1>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
// terminal case which returns the resulting MDTensor
|
||||
template <int R, int m = 0, int M = 0>
|
||||
inline MDTensor<R,T> MDReshape()
|
||||
{
|
||||
int k = 0, rLt_idx[M], rSd[M];
|
||||
// initialize sub_layout_pair's second
|
||||
for (sub_layout_type &sub: rLt)
|
||||
{
|
||||
for (sub_layout_pair &p: sub) { p.second = k++; }
|
||||
}
|
||||
// scan with the previous layout (N) order the reshaped layout (M)
|
||||
for (int i = 0, j = 0; i < N; i++)
|
||||
{
|
||||
for (sub_layout_pair &p: rLt[layout[i]])
|
||||
{
|
||||
rLt_idx[j++] = p.second;
|
||||
}
|
||||
}
|
||||
// apply the reshaped layout (M)
|
||||
rSd[rLt_idx[0]] = 1;
|
||||
for (int i = 1; i < M; i++)
|
||||
{
|
||||
rSd[rLt_idx[i]] = rNd[rLt_idx[i-1]] * rSd[rLt_idx[i-1]];
|
||||
}
|
||||
// construct the MDTensor with the given pointer and reshaped sizes
|
||||
static_assert(R == M, "R != M");
|
||||
return MDTensor<R,T>(reshape_ptr, rSd);
|
||||
}
|
||||
|
||||
private:
|
||||
T *reshape_ptr;
|
||||
std::vector<int> rNd; // reshape sizes
|
||||
int reshape_offset, reshape_shifts[2];// shift begin & end
|
||||
using sub_layout_pair = std::pair<int,int>;
|
||||
using sub_layout_type = std::list<sub_layout_pair>;
|
||||
std::array<sub_layout_type,N> rLt; // layout
|
||||
};
|
||||
|
||||
// md_sequence, md_extend and make_md_sequence implementation
|
||||
namespace internal
|
||||
{
|
||||
|
||||
template <typename T, int N, int mod, bool left> struct md_extend;
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 0, true>
|
||||
{
|
||||
using type = md_sequence<T, args..., (args + N)...>;
|
||||
};
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 1, true>
|
||||
{
|
||||
using type = md_sequence<T, args..., (args + N)..., 2*N>;
|
||||
};
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 0, false>
|
||||
{
|
||||
using type = md_sequence<T, (args + N)..., args...>;
|
||||
};
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 1, false>
|
||||
{
|
||||
using type = md_sequence<T, 2*N, (args + N)..., args...>;
|
||||
};
|
||||
|
||||
template <typename T, int N, bool L> struct make_md_sequence
|
||||
{
|
||||
using sequence_type = typename make_md_sequence<T,N/2,L>::type;
|
||||
using type = typename md_extend<sequence_type, N/2, N%2, L>::type;
|
||||
};
|
||||
|
||||
template <typename T, bool L>
|
||||
struct make_md_sequence<T,0,L> { using type = md_sequence<T>; };
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDSPAN_HPP
|
||||
+10
-58
@@ -408,26 +408,8 @@ class UvmHostMemorySpace : public HostMemorySpace
|
||||
{
|
||||
public:
|
||||
UvmHostMemorySpace(): HostMemorySpace() { }
|
||||
|
||||
void Alloc(void **ptr, size_t bytes) override
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
CuMallocManaged(ptr, bytes == 0 ? 8 : bytes);
|
||||
#endif
|
||||
#ifdef MFEM_USE_HIP
|
||||
HipMallocManaged(ptr, bytes == 0 ? 8 : bytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Dealloc(void *ptr) override
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
CuMemFree(ptr);
|
||||
#endif
|
||||
#ifdef MFEM_USE_HIP
|
||||
HipMemFree(ptr);
|
||||
#endif
|
||||
}
|
||||
void Alloc(void **ptr, size_t bytes) override { CuMallocManaged(ptr, bytes == 0 ? 8 : bytes); }
|
||||
void Dealloc(void *ptr) override { CuMemFree(ptr); }
|
||||
};
|
||||
|
||||
/// The 'No' device memory space
|
||||
@@ -522,25 +504,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class UvmHipMemorySpace : public DeviceMemorySpace
|
||||
{
|
||||
public:
|
||||
void Alloc(Memory &base) { base.d_ptr = base.h_ptr; }
|
||||
void Dealloc(Memory&) { }
|
||||
void *HtoD(void *dst, const void *src, size_t bytes)
|
||||
{
|
||||
if (dst == src) { MFEM_STREAM_SYNC; return dst; }
|
||||
return HipMemcpyHtoD(dst, src, bytes);
|
||||
}
|
||||
void *DtoD(void* dst, const void* src, size_t bytes)
|
||||
{ return HipMemcpyDtoD(dst, src, bytes); }
|
||||
void *DtoH(void *dst, const void *src, size_t bytes)
|
||||
{
|
||||
if (dst == src) { MFEM_STREAM_SYNC; return dst; }
|
||||
return HipMemcpyDtoH(dst, src, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
/// The MMU device memory space
|
||||
class MmuDeviceMemorySpace : public DeviceMemorySpace
|
||||
{
|
||||
@@ -698,15 +661,7 @@ public:
|
||||
|
||||
// Filling the device memory backends, shifting with the device size
|
||||
constexpr int shift = DeviceMemoryType;
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
device[static_cast<int>(MT::MANAGED)-shift] = new UvmCudaMemorySpace();
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
device[static_cast<int>(MT::MANAGED)-shift] = new UvmHipMemorySpace();
|
||||
#else
|
||||
// this re-creates the original behavior, but should this be nullptr instead?
|
||||
device[static_cast<int>(MT::MANAGED)-shift] = new UvmCudaMemorySpace();
|
||||
#endif
|
||||
|
||||
// All other devices controllers are delayed
|
||||
device[static_cast<int>(MemoryType::DEVICE)-shift] = nullptr;
|
||||
device[static_cast<int>(MT::DEVICE_DEBUG)-shift] = nullptr;
|
||||
@@ -1238,9 +1193,8 @@ void MemoryManager::Copy_(void *dst_h_ptr, const void *src_h_ptr,
|
||||
{
|
||||
if (dst_h_ptr != src_d_ptr && bytes != 0)
|
||||
{
|
||||
MemoryType src_d_mt = (src_flags & Mem::ALIAS) ?
|
||||
maps->aliases.at(src_h_ptr).mem->d_mt :
|
||||
maps->memories.at(src_h_ptr).d_mt;
|
||||
internal::Memory &src_d_base = maps->memories.at(src_h_ptr);
|
||||
MemoryType src_d_mt = src_d_base.d_mt;
|
||||
ctrl->Device(src_d_mt)->DtoH(dst_h_ptr, src_d_ptr, bytes);
|
||||
}
|
||||
}
|
||||
@@ -1300,10 +1254,9 @@ void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
const void *src_d_ptr = (src_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(src_h_ptr, bytes, false) :
|
||||
mm.GetDevicePtr(src_h_ptr, bytes, false);
|
||||
MemoryType src_d_mt = (src_flags & Mem::ALIAS) ?
|
||||
maps->aliases.at(src_h_ptr).mem->d_mt :
|
||||
maps->memories.at(src_h_ptr).d_mt;
|
||||
ctrl->Device(src_d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
|
||||
const internal::Memory &base = maps->memories.at(dest_h_ptr);
|
||||
const MemoryType d_mt = base.d_mt;
|
||||
ctrl->Device(d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1330,10 +1283,9 @@ void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
void *dest_d_ptr = (dest_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(dest_h_ptr, bytes, false) :
|
||||
mm.GetDevicePtr(dest_h_ptr, bytes, false);
|
||||
MemoryType dest_d_mt = (dest_flags & Mem::ALIAS) ?
|
||||
maps->aliases.at(dest_h_ptr).mem->d_mt :
|
||||
maps->memories.at(dest_h_ptr).d_mt;
|
||||
ctrl->Device(dest_d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
|
||||
const internal::Memory &base = maps->memories.at(dest_h_ptr);
|
||||
const MemoryType d_mt = base.d_mt;
|
||||
ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
|
||||
}
|
||||
dest_flags = dest_flags &
|
||||
~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
|
||||
|
||||
@@ -169,7 +169,6 @@ class Memory
|
||||
protected:
|
||||
friend class MemoryManager;
|
||||
friend void MemoryPrintFlags(unsigned flags);
|
||||
template<typename mfem_type, int N, typename L> friend class MDSpan;
|
||||
|
||||
enum FlagMask: unsigned
|
||||
{
|
||||
|
||||
+48
-13
@@ -15,45 +15,80 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
int IntegerSet::PickRandomElement() const
|
||||
IntegerSet::IntegerSet(IntegerSet &s)
|
||||
: me(s.me.Size())
|
||||
{
|
||||
int i, size = Size();
|
||||
for (int i = 0; i < me.Size(); i++)
|
||||
{
|
||||
me[i] = s.me[i];
|
||||
}
|
||||
}
|
||||
|
||||
IntegerSet& IntegerSet::operator=(const IntegerSet &s)
|
||||
{
|
||||
me.SetSize(s.me.Size());
|
||||
for (int i = 0; i < me.Size(); i++)
|
||||
{
|
||||
me[i] = s.me[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
int IntegerSet::operator== (IntegerSet &s)
|
||||
{
|
||||
if (me.Size() != s.me.Size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < me.Size(); i++)
|
||||
if (me[i] != s.me[i])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int IntegerSet::PickRandomElement()
|
||||
{
|
||||
int i, size = me.Size();
|
||||
unsigned int seed = 0;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
seed += data[i];
|
||||
seed += me[i];
|
||||
}
|
||||
|
||||
srand(seed);
|
||||
|
||||
return data[rand()/(RAND_MAX/size)];
|
||||
return me[rand()/(RAND_MAX/size)];
|
||||
}
|
||||
|
||||
void IntegerSet::Recreate(const int n, const int *p)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
SetSize(n);
|
||||
me.SetSize(n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
data[i] = p[i];
|
||||
me[i] = p[i];
|
||||
}
|
||||
|
||||
Sort();
|
||||
me.Sort();
|
||||
|
||||
for (j = 0, i = 1; i < n; i++)
|
||||
if (data[i] != data[j])
|
||||
if (me[i] != me[j])
|
||||
{
|
||||
data[++j] = data[i];
|
||||
me[++j] = me[i];
|
||||
}
|
||||
|
||||
SetSize(j+1);
|
||||
me.SetSize(j+1);
|
||||
}
|
||||
|
||||
|
||||
int ListOfIntegerSets::Insert(const IntegerSet &s)
|
||||
int ListOfIntegerSets::Insert(IntegerSet &s)
|
||||
{
|
||||
for (int i = 0; i < TheList.Size(); i++)
|
||||
if (*TheList[i] == s)
|
||||
@@ -66,7 +101,7 @@ int ListOfIntegerSets::Insert(const IntegerSet &s)
|
||||
return TheList.Size()-1;
|
||||
}
|
||||
|
||||
int ListOfIntegerSets::Lookup(const IntegerSet &s) const
|
||||
int ListOfIntegerSets::Lookup(IntegerSet &s)
|
||||
{
|
||||
for (int i = 0; i < TheList.Size(); i++)
|
||||
if (*TheList[i] == s)
|
||||
@@ -78,7 +113,7 @@ int ListOfIntegerSets::Lookup(const IntegerSet &s) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ListOfIntegerSets::AsTable(Table & t) const
|
||||
void ListOfIntegerSets::AsTable(Table & t)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
+29
-17
@@ -20,26 +20,38 @@ namespace mfem
|
||||
{
|
||||
|
||||
/// A set of integers
|
||||
class IntegerSet : public Array<int>
|
||||
class IntegerSet
|
||||
{
|
||||
private:
|
||||
Array<int> me;
|
||||
|
||||
public:
|
||||
using Array<int>::Array; ///< Inherit all Array constructors.
|
||||
// MSVC fails to recognize that rule of zero applies after using base class
|
||||
// constructors.
|
||||
IntegerSet() = default; ///< Default construct and empty set.
|
||||
IntegerSet(const IntegerSet &) = default; ///< Copy constructor.
|
||||
IntegerSet(IntegerSet &&) = default; ///< Move constructor.
|
||||
IntegerSet& operator=(const IntegerSet &) = default; ///< Copy assignment.
|
||||
IntegerSet& operator=(IntegerSet &&) = default; ///< Move assignment.
|
||||
/// Create an empty set.
|
||||
IntegerSet() { }
|
||||
|
||||
/// Create a copy of set 's'.
|
||||
IntegerSet(IntegerSet &s);
|
||||
|
||||
/// Create an integer set from C-array 'p' of 'n' integers.
|
||||
IntegerSet(const int n, const int *p) { Recreate(n, p); }
|
||||
|
||||
/// Return the size of the set.
|
||||
int Size() { return me.Size(); }
|
||||
|
||||
/// Return a reference to the sorted array of all the set entries.
|
||||
operator Array<int>& () { return me; }
|
||||
|
||||
/// Return the value of the lowest element of the set.
|
||||
int PickElement() const { return data[0]; }
|
||||
int PickElement() { return me[0]; }
|
||||
|
||||
/// Return the value of a random element of the set.
|
||||
int PickRandomElement() const;
|
||||
int PickRandomElement();
|
||||
|
||||
/// Create a copy of set 's'.
|
||||
IntegerSet& operator=(const IntegerSet &s);
|
||||
|
||||
/// Return 1 if the sets are equal and 0 otherwise.
|
||||
int operator==(IntegerSet &s);
|
||||
|
||||
/** @brief Create an integer set from C-array 'p' of 'n' integers.
|
||||
Overwrites any existing set data. */
|
||||
@@ -55,25 +67,25 @@ private:
|
||||
public:
|
||||
|
||||
/// Return the number of integer sets in the list.
|
||||
int Size() const { return TheList.Size(); }
|
||||
int Size() { return TheList.Size(); }
|
||||
|
||||
/// Return the value of the first element of the ith set.
|
||||
int PickElementInSet(int i) const { return TheList[i]->PickElement(); }
|
||||
int PickElementInSet(int i) { return TheList[i]->PickElement(); }
|
||||
|
||||
/// Return a random value from the ith set in the list.
|
||||
int PickRandomElementInSet(int i) const { return TheList[i]->PickRandomElement(); }
|
||||
int PickRandomElementInSet(int i) { return TheList[i]->PickRandomElement(); }
|
||||
|
||||
/** @brief Check to see if set 's' is in the list. If not append it to the
|
||||
end of the list. Returns the index of the list where set 's' can be
|
||||
found. */
|
||||
int Insert(const IntegerSet &s);
|
||||
int Insert(IntegerSet &s);
|
||||
|
||||
/** Return the index of the list where set 's' can be found. Returns -1 if
|
||||
not found. */
|
||||
int Lookup(const IntegerSet &s) const;
|
||||
int Lookup(IntegerSet &s);
|
||||
|
||||
/// Write the list of sets into table 't'.
|
||||
void AsTable(Table &t) const;
|
||||
void AsTable(Table &t);
|
||||
|
||||
~ListOfIntegerSets();
|
||||
};
|
||||
|
||||
@@ -57,7 +57,6 @@ list(APPEND HDRS
|
||||
lapack.hpp
|
||||
linalg.hpp
|
||||
matrix.hpp
|
||||
mdvector.hpp
|
||||
ode.hpp
|
||||
operator.hpp
|
||||
solvers.hpp
|
||||
|
||||
@@ -52,9 +52,9 @@ BatchedLinAlg &BatchedLinAlg::Instance()
|
||||
}
|
||||
|
||||
void BatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha, real_t beta, Op op)
|
||||
real_t alpha, real_t beta)
|
||||
{
|
||||
Get(Instance().active_backend).AddMult(A, x, y, alpha, beta, op);
|
||||
Get(Instance().active_backend).AddMult(A, x, y, alpha, beta);
|
||||
}
|
||||
|
||||
void BatchedLinAlg::Mult(const DenseTensor &A, const Vector &x, Vector &y)
|
||||
@@ -62,12 +62,6 @@ void BatchedLinAlg::Mult(const DenseTensor &A, const Vector &x, Vector &y)
|
||||
Get(Instance().active_backend).Mult(A, x, y);
|
||||
}
|
||||
|
||||
void BatchedLinAlg::MultTranspose(const DenseTensor &A, const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
Get(Instance().active_backend).MultTranspose(A, x, y);
|
||||
}
|
||||
|
||||
void BatchedLinAlg::Invert(DenseTensor &A)
|
||||
{
|
||||
Get(Instance().active_backend).Invert(A);
|
||||
@@ -113,10 +107,4 @@ void BatchedLinAlgBase::Mult(const DenseTensor &A, const Vector &x,
|
||||
AddMult(A, x, y, 1.0, 0.0);
|
||||
}
|
||||
|
||||
void BatchedLinAlgBase::MultTranspose(const DenseTensor &A, const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
AddMult(A, x, y, 1.0, 0.0, Op::T);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,14 +48,6 @@ public:
|
||||
/// Counter for the number of backends.
|
||||
NUM_BACKENDS
|
||||
};
|
||||
|
||||
/// Operation type (transposed or not transposed)
|
||||
enum Op
|
||||
{
|
||||
N, ///< Not transposed.
|
||||
T ///< Transposed.
|
||||
};
|
||||
|
||||
private:
|
||||
/// All available backends. Unavailble backends will be nullptr.
|
||||
std::array<std::unique_ptr<class BatchedLinAlgBase>,
|
||||
@@ -66,19 +58,15 @@ private:
|
||||
/// Return the singleton instance.
|
||||
static BatchedLinAlg &Instance();
|
||||
public:
|
||||
/// @brief Computes $y = \alpha A^{op} x + \beta y$.
|
||||
/// @brief Computes $y = \alpha A x + \beta y$.
|
||||
///
|
||||
/// $A^{op}$ is either $A$ or $A^T$ depending on the value of @a op.
|
||||
/// $A$ is a block diagonal matrix, represented by the DenseTensor @a A with
|
||||
/// shape (m, n, n_mat). $x$ has shape (tr?m:n, k, n_mat), and $y$ has shape
|
||||
/// (tr?n:m, k, n_mat), where 'tr' is true in the transposed case.
|
||||
/// shape (m, n, n_mat). $x$ has shape (n, k, n_mat), and $y$ has shape
|
||||
/// (m, k, n_mat).
|
||||
static void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N);
|
||||
/// Computes $y = A x$ (e.g. by calling @ref AddMult "AddMult(A,x,y,1,0,Op::N)").
|
||||
real_t alpha = 1.0, real_t beta = 1.0);
|
||||
/// Computes $y = A x$ (e.g. by calling @ref AddMult "AddMult(A,x,y,1,0)").
|
||||
static void Mult(const DenseTensor &A, const Vector &x, Vector &y);
|
||||
/// Computes $y = A^T x$ (e.g. by calling @ref AddMult "AddMult(A,x,y,1,0,Op::T)").
|
||||
static void MultTranspose(const DenseTensor &A, const Vector &x, Vector &y);
|
||||
/// @brief Replaces the block diagonal matrix $A$ with its inverse $A^{-1}$.
|
||||
///
|
||||
/// $A$ is represented by the DenseTensor @a A with shape (m, m, n_mat).
|
||||
@@ -121,16 +109,11 @@ public:
|
||||
class BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
using Op = BatchedLinAlg::Op;
|
||||
/// See BatchedLinAlg::AddMult.
|
||||
virtual void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N) const = 0;
|
||||
real_t alpha = 1.0, real_t beta = 1.0) const = 0;
|
||||
/// See BatchedLinAlg::Mult.
|
||||
virtual void Mult(const DenseTensor &A, const Vector &x, Vector &y) const;
|
||||
/// See BatchedLinAlg::MultTranspose.
|
||||
virtual void MultTranspose(const DenseTensor &A, const Vector &x,
|
||||
Vector &y) const;
|
||||
/// See BatchedLinAlg::Invert.
|
||||
virtual void Invert(DenseTensor &A) const = 0;
|
||||
/// See BatchedLinAlg::LUFactor.
|
||||
|
||||
+10
-14
@@ -82,27 +82,23 @@ void GPUBlas::DisableAtomics()
|
||||
}
|
||||
|
||||
void GPUBlasBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
Vector &y, real_t alpha, real_t beta,
|
||||
Op op) const
|
||||
Vector &y, real_t alpha, real_t beta) const
|
||||
{
|
||||
const bool tr = (op == Op::T);
|
||||
|
||||
const int m = tr ? A.SizeJ() : A.SizeI();
|
||||
const int n = tr ? A.SizeI() : A.SizeJ();
|
||||
const int m = A.SizeI();
|
||||
const int n = A.SizeJ();
|
||||
const int n_mat = A.SizeK();
|
||||
const int k = x.Size() / n / n_mat;
|
||||
|
||||
auto d_A = A.Read();
|
||||
auto d_x = x.Read(); // Shape: (n, k, n_mat)
|
||||
auto d_y = beta == 0.0 ? y.Write() : y.ReadWrite(); // Shape (m, k, n_mat)
|
||||
auto d_A = mfem::Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Read(), n, k, n_mat);
|
||||
auto d_y = mfem::Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(), m, k, n_mat);
|
||||
|
||||
const auto op_A = tr ? MFEM_CU_or_HIP(BLAS_OP_T) : MFEM_CU_or_HIP(BLAS_OP_N);
|
||||
const auto op_B = MFEM_CU_or_HIP(BLAS_OP_N);
|
||||
const auto op = MFEM_CU_or_HIP(BLAS_OP_N);
|
||||
|
||||
const blasStatus_t status = MFEM_GPUBLAS_PREFIX(gemmStridedBatched)(
|
||||
GPUBlas::Handle(), op_A, op_B, m, k, n,
|
||||
&alpha, d_A, m, m*n, d_x, n, n*k, &beta, d_y,
|
||||
m, m*k, n_mat);
|
||||
GPUBlas::Handle(), op, op, m, k, n, &alpha,
|
||||
d_A, m, m*n, d_x, n, n*k, &beta, d_y, m, m*k,
|
||||
n_mat);
|
||||
MFEM_VERIFY(status == MFEM_BLAS_SUCCESS, "GPU BLAS error.");
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,7 @@ class GPUBlasBatchedLinAlg : public BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N) const override;
|
||||
real_t alpha = 1.0, real_t beta = 1.0) const override;
|
||||
void Invert(DenseTensor &A) const override;
|
||||
void LUFactor(DenseTensor &A, Array<int> &P) const override;
|
||||
void LUSolve(const DenseTensor &LU, const Array<int> &P,
|
||||
|
||||
@@ -54,24 +54,19 @@ magma_queue_t Magma::Queue()
|
||||
}
|
||||
|
||||
void MagmaBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
Vector &y, real_t alpha, real_t beta,
|
||||
Op op) const
|
||||
Vector &y, real_t alpha, real_t beta) const
|
||||
{
|
||||
const bool tr = (op == Op::T);
|
||||
|
||||
const int m = tr ? A.SizeJ() : A.SizeI();
|
||||
const int n = tr ? A.SizeI() : A.SizeJ();
|
||||
const int m = A.SizeI();
|
||||
const int n = A.SizeJ();
|
||||
const int n_mat = A.SizeK();
|
||||
const int k = x.Size() / n / n_mat;
|
||||
|
||||
auto d_A = A.Read();
|
||||
auto d_x = x.Read(); // Shape (n, k, n_mat);
|
||||
auto d_y = beta == 0.0 ? y.Write() : y.ReadWrite(); // Shape (m, k, n_mat);
|
||||
|
||||
magma_trans_t magma_op = tr ? MagmaNoTrans : MagmaTrans;
|
||||
auto d_A = mfem::Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Read(), n, k, n_mat);
|
||||
auto d_y = mfem::Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(), m, k, n_mat);
|
||||
|
||||
MFEM_MAGMABLAS_PREFIX(gemm_batched_strided)(
|
||||
magma_op, MagmaNoTrans, m, k, n, alpha, d_A, m, m*n, d_x, n, n*k,
|
||||
MagmaNoTrans, MagmaNoTrans, m, k, n, alpha, d_A, m, m*n, d_x, n, n*k,
|
||||
beta, d_y, m, m*k, n_mat, Magma::Queue());
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ class MagmaBatchedLinAlg : public BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N) const override;
|
||||
real_t alpha = 1.0, real_t beta = 1.0) const override;
|
||||
void Invert(DenseTensor &A) const override;
|
||||
void LUFactor(DenseTensor &A, Array<int> &P) const override;
|
||||
void LUSolve(const DenseTensor &A, const Array<int> &P,
|
||||
|
||||
+17
-110
@@ -18,37 +18,22 @@ namespace mfem
|
||||
{
|
||||
|
||||
void NativeBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
Vector &y, real_t alpha, real_t beta,
|
||||
Op op) const
|
||||
Vector &y, real_t alpha, real_t beta) const
|
||||
{
|
||||
const bool tr = (op == Op::T);
|
||||
|
||||
const int m = A.SizeI();
|
||||
const int n = A.SizeJ();
|
||||
const int n_mat = A.SizeK();
|
||||
const int k = x.Size() / (tr ? m : n) / n_mat;
|
||||
const int k = x.Size() / n / n_mat;
|
||||
|
||||
auto d_A = Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = Reshape(x.Read(), (tr ? m : n), k, n_mat);
|
||||
auto d_y = Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(),
|
||||
(tr ? n : m), k, n_mat);
|
||||
auto d_A = mfem::Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Read(), n, k, n_mat);
|
||||
auto d_y = mfem::Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(), m, k, n_mat);
|
||||
|
||||
if (tr)
|
||||
mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
kernels::AddMultAtB(m, n, k, &d_A(0,0,i), &d_x(0,0,i), &d_y(0,0,i),
|
||||
alpha, beta);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
kernels::AddMult(m, k, n, &d_A(0,0,i), &d_x(0,0,i), &d_y(0,0,i),
|
||||
alpha, beta);
|
||||
});
|
||||
}
|
||||
kernels::AddMult(m, k, n, &d_A(0,0,i), &d_x(0,0,i), &d_y(0,0,i),
|
||||
alpha, beta);
|
||||
});
|
||||
|
||||
// Alternative approach, threading also over the second index. Which one is
|
||||
// better?
|
||||
@@ -63,85 +48,7 @@ void NativeBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
|
||||
void NativeBatchedLinAlg::Invert(DenseTensor &A) const
|
||||
{
|
||||
const int m = A.SizeI();
|
||||
const int NE = A.SizeK();
|
||||
DenseTensor LU = A;
|
||||
Array<int> P(m*NE);
|
||||
|
||||
LUFactor(LU, P);
|
||||
|
||||
auto data_all = Reshape(LU.Read(), m, m, NE);
|
||||
auto piv_all = Reshape(P.Read(), m, NE);
|
||||
auto inv_all = Reshape(A.Write(), m, m, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// A^{-1} = U^{-1} L^{-1} P
|
||||
// X <- U^{-1} (set only the upper triangular part of X)
|
||||
real_t *X = &inv_all(0, 0, e);
|
||||
real_t *x = X;
|
||||
const real_t *data = &data_all(0, 0, e);
|
||||
const int *ipiv = &piv_all(0, e);
|
||||
|
||||
for (int k = 0; k < m; k++)
|
||||
{
|
||||
const real_t minus_x_k = -(x[k] = 1.0 / data[k + k * m]);
|
||||
for (int i = 0; i < k; i++)
|
||||
{
|
||||
x[i] = data[i + k * m] * minus_x_k;
|
||||
}
|
||||
for (int j = k - 1; j >= 0; j--)
|
||||
{
|
||||
const real_t x_j = (x[j] /= data[j + j * m]);
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
x[i] -= data[i + j * m] * x_j;
|
||||
}
|
||||
}
|
||||
x += m;
|
||||
}
|
||||
|
||||
// X <- X L^{-1} (use input only from the upper triangular part of X)
|
||||
{
|
||||
int k = m - 1;
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
const real_t minus_L_kj = -data[k + j * m];
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
X[i + j * m] += X[i + k * m] * minus_L_kj;
|
||||
}
|
||||
for (int i = j + 1; i < m; i++)
|
||||
{
|
||||
X[i + j * m] = X[i + k * m] * minus_L_kj;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int k = m - 2; k >= 0; k--)
|
||||
{
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
const real_t L_kj = data[k + j * m];
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
X[i + j * m] -= X[i + k * m] * L_kj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// X <- X P
|
||||
for (int k = m - 1; k >= 0; k--)
|
||||
{
|
||||
const int piv_k = ipiv[k];
|
||||
if (k != piv_k)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
kernels::internal::Swap(X[i + k * m], X[i + piv_k * m]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
void NativeBatchedLinAlg::LUFactor(DenseTensor &A, Array<int> &P) const
|
||||
@@ -151,8 +58,8 @@ void NativeBatchedLinAlg::LUFactor(DenseTensor &A, Array<int> &P) const
|
||||
const int NE = A.SizeK();
|
||||
P.SetSize(m*NE);
|
||||
|
||||
auto data_all = Reshape(A.ReadWrite(), m, m, NE);
|
||||
auto ipiv_all = Reshape(P.Write(), m, NE);
|
||||
auto data_all = mfem::Reshape(A.ReadWrite(), m, m, NE);
|
||||
auto ipiv_all = mfem::Reshape(P.Write(), m, NE);
|
||||
Array<bool> pivot_flag(1);
|
||||
pivot_flag[0] = true;
|
||||
bool *d_pivot_flag = pivot_flag.ReadWrite();
|
||||
@@ -180,12 +87,12 @@ void NativeBatchedLinAlg::LUFactor(DenseTensor &A, Array<int> &P) const
|
||||
// swap rows i and piv in both L and U parts
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
kernels::internal::Swap<real_t>(data_all(i,j,e), data_all(piv,j,e));
|
||||
mfem::kernels::internal::Swap<real_t>(data_all(i,j,e), data_all(piv,j,e));
|
||||
}
|
||||
}
|
||||
} // pivot end
|
||||
|
||||
if (std::abs(data_all(i,i,e)) <= tol)
|
||||
if (abs(data_all(i,i,e)) <= tol)
|
||||
{
|
||||
d_pivot_flag[0] = false;
|
||||
}
|
||||
@@ -217,9 +124,9 @@ void NativeBatchedLinAlg::LUSolve(const DenseTensor &LU, const Array<int> &P,
|
||||
const int n_mat = LU.SizeK();
|
||||
const int n_rhs = x.Size() / m / n_mat;
|
||||
|
||||
auto d_LU = Reshape(LU.Read(), m, m, n_mat);
|
||||
auto d_P = Reshape(P.Read(), m, n_mat);
|
||||
auto d_x = Reshape(x.Write(), m, n_rhs, n_mat);
|
||||
auto d_LU = mfem::Reshape(LU.Read(), m, m, n_mat);
|
||||
auto d_P = mfem::Reshape(P.Read(), m, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Write(), m, n_rhs, n_mat);
|
||||
|
||||
mfem::forall(n_mat * n_rhs, [=] MFEM_HOST_DEVICE (int idx)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ class NativeBatchedLinAlg : public BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha, real_t beta, Op op) const override;
|
||||
real_t alpha, real_t beta) const override;
|
||||
void Invert(DenseTensor &A) const override;
|
||||
void LUFactor(DenseTensor &A, Array<int> &P) const override;
|
||||
void LUSolve(const DenseTensor &LU, const Array<int> &P,
|
||||
|
||||
@@ -357,4 +357,154 @@ BlockLowerTriangularPreconditioner::~BlockLowerTriangularPreconditioner()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BlockTriangularSymmetricPreconditioner::BlockTriangularSymmetricPreconditioner(
|
||||
const Array<int> & offsets_)
|
||||
: Solver(offsets_.Last()),
|
||||
owns_blocks(0),
|
||||
nBlocks(offsets_.Size() - 1),
|
||||
offsets(0),
|
||||
ops(nBlocks, nBlocks)
|
||||
{
|
||||
ops = static_cast<Operator *>(NULL);
|
||||
offsets.MakeRef(offsets_);
|
||||
}
|
||||
|
||||
void BlockTriangularSymmetricPreconditioner::SetDiagonalBlock(int iblock,
|
||||
Operator *op)
|
||||
{
|
||||
MFEM_VERIFY(offsets[iblock+1] - offsets[iblock] == op->Height() &&
|
||||
offsets[iblock+1] - offsets[iblock] == op->Width(),
|
||||
"incompatible Operator dimensions");
|
||||
|
||||
SetBlock(iblock, iblock, op);
|
||||
}
|
||||
|
||||
void BlockTriangularSymmetricPreconditioner::SetBlock(int iRow, int iCol,
|
||||
Operator *op)
|
||||
{
|
||||
MFEM_VERIFY(offsets[iRow+1] - offsets[iRow] == op->NumRows() &&
|
||||
offsets[iCol+1] - offsets[iCol] == op->NumCols(),
|
||||
"incompatible Operator dimensions");
|
||||
|
||||
ops(iRow, iCol) = op;
|
||||
}
|
||||
|
||||
// Operator application
|
||||
|
||||
void BlockTriangularSymmetricPreconditioner::ForwardPass(const Vector & x,
|
||||
Vector & y) const
|
||||
{
|
||||
// Forward sweep: Solve for y1, then y2
|
||||
for (int iRow = 0; iRow < nBlocks; ++iRow)
|
||||
{
|
||||
tmp.SetSize(offsets[iRow + 1] - offsets[iRow]);
|
||||
tmp2.SetSize(offsets[iRow + 1] - offsets[iRow]);
|
||||
tmp2 = 0.0;
|
||||
tmp2 += xblock.GetBlock(iRow); // tmp2 = xblock(iRow)
|
||||
|
||||
// Process the lower triangular part (jCol < iRow)
|
||||
for (int jCol = 0; jCol < iRow; ++jCol)
|
||||
{
|
||||
if (ops(iRow, jCol))
|
||||
{
|
||||
ops(iRow, jCol)->Mult(yblock.GetBlock(jCol),
|
||||
tmp); // tmp = A(iRow,jCol) * yblock(jCol)
|
||||
tmp2 -= tmp; // tmp2 -= A(iRow, jCol) * yblock(jCol)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the diagonal block
|
||||
if (ops(iRow, iRow))
|
||||
{
|
||||
ops(iRow, iRow)->Mult(tmp2,
|
||||
yblock.GetBlock(iRow)); // yblock(iRow) = A(iRow,iRow)^-1 * tmp2
|
||||
}
|
||||
else
|
||||
{
|
||||
yblock.GetBlock(iRow) = tmp2; // If no diagonal operator, set yblock directly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockTriangularSymmetricPreconditioner::BackwardPass(const Vector & x,
|
||||
Vector & y) const
|
||||
{
|
||||
// Backward sweep: Adjust y1 based on y2
|
||||
for (int iRow = nBlocks - 1; iRow >= 0; --iRow)
|
||||
{
|
||||
tmp.SetSize(offsets[iRow + 1] - offsets[iRow]);
|
||||
tmp2.SetSize(offsets[iRow + 1] - offsets[iRow]);
|
||||
tmp2 = 0.0;
|
||||
tmp2 += xblock.GetBlock(iRow); // tmp2 = yblock(iRow) from forward sweep
|
||||
|
||||
// Process the upper triangular part (jCol > iRow)
|
||||
for (int jCol = iRow + 1; jCol < nBlocks; ++jCol)
|
||||
{
|
||||
if (ops(iRow, jCol))
|
||||
{
|
||||
ops(iRow, jCol)->Mult(yblock.GetBlock(jCol),
|
||||
tmp); // tmp = A(iRow,jCol) * yblock(jCol)
|
||||
tmp2 -= tmp; // tmp2 -= A(iRow,jCol) * yblock(jCol)
|
||||
}
|
||||
}
|
||||
|
||||
// Reapply diagonal block to correct y1
|
||||
if (ops(iRow, iRow))
|
||||
{
|
||||
ops(iRow, iRow)->Mult(tmp2,
|
||||
yblock.GetBlock(iRow)); // Final correction for yblock(iRow)
|
||||
}
|
||||
else
|
||||
{
|
||||
yblock.GetBlock(iRow) = tmp2; // If no diagonal operator, set yblock directly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BlockTriangularSymmetricPreconditioner::Mult (const Vector & x,
|
||||
Vector & y) const
|
||||
{
|
||||
MFEM_ASSERT(x.Size() == width, "incorrect input Vector size");
|
||||
MFEM_ASSERT(y.Size() == height, "incorrect output Vector size");
|
||||
|
||||
// Update block views of the vectors y and x using offsets
|
||||
yblock.Update(y.GetData(), offsets);
|
||||
xblock.Update(x.GetData(), offsets);
|
||||
|
||||
// Initialize y to zero
|
||||
y = 0.0;
|
||||
ForwardPass(x,y);
|
||||
|
||||
// Update Residual
|
||||
r.SetSize(x.Size());
|
||||
r = 0.0; r+=x;
|
||||
Op->AddMult(y,r,-1.0);
|
||||
|
||||
Vector y1(y);
|
||||
yblock.Update(y1.GetData(), offsets);
|
||||
xblock.Update(r.GetData(), offsets);
|
||||
BackwardPass(r,y1);
|
||||
y+=y1;
|
||||
}
|
||||
|
||||
BlockTriangularSymmetricPreconditioner::~BlockTriangularSymmetricPreconditioner()
|
||||
{
|
||||
if (owns_blocks)
|
||||
{
|
||||
for (int iRow=0; iRow < nBlocks; ++iRow)
|
||||
{
|
||||
for (int jCol=0; jCol < nBlocks; ++jCol)
|
||||
{
|
||||
delete ops(jCol,iRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -288,6 +288,78 @@ private:
|
||||
mutable Vector tmp2;
|
||||
};
|
||||
|
||||
|
||||
class BlockTriangularSymmetricPreconditioner : public Solver
|
||||
{
|
||||
private:
|
||||
const Operator * Op;
|
||||
public:
|
||||
//! Constructor for BlockTriangularSymmetricPreconditioners with the same
|
||||
//! block-structure for rows and columns.
|
||||
/**
|
||||
* @param offsets Offsets that mark the start of each row/column block
|
||||
* (size nBlocks+1).
|
||||
*
|
||||
* @note BlockTriangularSymmetricPreconditioner will not own/copy the data
|
||||
* contained in @a offsets.
|
||||
*/
|
||||
BlockTriangularSymmetricPreconditioner(const Array<int> & offsets);
|
||||
|
||||
//! Add block op in the block-entry (iblock, iblock).
|
||||
/**
|
||||
* @param iblock The block will be inserted in location (iblock, iblock).
|
||||
* @param op The Operator to be inserted.
|
||||
*/
|
||||
void SetDiagonalBlock(int iblock, Operator *op);
|
||||
//! Add a block opt in the block-entry (iblock, jblock).
|
||||
/**
|
||||
* @param iRow, iCol The block will be inserted in location (iRow, iCol).
|
||||
* @param op The Operator to be inserted.
|
||||
*/
|
||||
void SetBlock(int iRow, int iCol, Operator *op);
|
||||
//! This method is present since required by the abstract base class Solver
|
||||
virtual void SetOperator(const Operator &op) {Op = &op;}
|
||||
|
||||
//! Return the number of blocks
|
||||
int NumBlocks() const { return nBlocks; }
|
||||
|
||||
//! Return a reference to block i,j.
|
||||
Operator & GetBlock(int iblock, int jblock)
|
||||
{ MFEM_VERIFY(ops(iblock,jblock), ""); return *ops(iblock,jblock); }
|
||||
|
||||
//! Return the offsets for block starts
|
||||
Array<int> & Offsets() { return offsets; }
|
||||
|
||||
/// Operator application
|
||||
virtual void Mult (const Vector & x, Vector & y) const;
|
||||
|
||||
~BlockTriangularSymmetricPreconditioner();
|
||||
|
||||
//! Controls the ownership of the blocks: if nonzero,
|
||||
//! BlockTriangularSymmetricPreconditioner will delete all blocks that are set
|
||||
//! (non-NULL); the default value is zero.
|
||||
int owns_blocks;
|
||||
|
||||
private:
|
||||
//! Number of block rows/columns
|
||||
int nBlocks;
|
||||
//! Offsets for the starting position of each block
|
||||
Array<int> offsets;
|
||||
//! 2D array that stores each block of the operator.
|
||||
Array2D<Operator *> ops;
|
||||
|
||||
//! Temporary Vectors used to efficiently apply the Mult and MultTranspose
|
||||
//! methods.
|
||||
mutable BlockVector xblock;
|
||||
mutable BlockVector yblock;
|
||||
mutable Vector tmp;
|
||||
mutable Vector tmp2;
|
||||
mutable Vector r;
|
||||
void ForwardPass(const Vector & x, Vector & y) const;
|
||||
void BackwardPass(const Vector & x, Vector & y) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* MFEM_BLOCKOPERATOR */
|
||||
|
||||
@@ -1174,31 +1174,6 @@ public:
|
||||
tdata.Wrap(ext_data, i*j*k, false);
|
||||
}
|
||||
|
||||
/// @brief Reset the DenseTensor to use the given external Memory @a mem and
|
||||
/// dimensions @a i, @a j, and @a k.
|
||||
///
|
||||
/// If @a own_mem is false, the DenseTensor will not own any of the pointers
|
||||
/// of @a mem.
|
||||
///
|
||||
/// Note that when @a own_mem is true, the @a mem object can be destroyed
|
||||
/// immediately by the caller but `mem.Delete()` should NOT be called since
|
||||
/// the DenseTensor object takes ownership of all pointers owned by @a mem.
|
||||
void NewMemoryAndSize(const Memory<real_t> &mem, int i, int j, int k,
|
||||
bool own_mem)
|
||||
{
|
||||
tdata.Delete();
|
||||
Mk.UseExternalData(NULL, i, j);
|
||||
nk = k;
|
||||
if (own_mem)
|
||||
{
|
||||
tdata = mem;
|
||||
}
|
||||
else
|
||||
{
|
||||
tdata.MakeAlias(mem, 0, i*j*k);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the tensor elements equal to constant c
|
||||
DenseTensor &operator=(real_t c);
|
||||
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@
|
||||
|
||||
// Make sure that hypre and PETSc use the same size indices.
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_PETSC)
|
||||
#if ((defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT)) && !defined(PETSC_USE_64BIT_INDICES)) || \
|
||||
(!defined(HYPRE_BIGINT) && !defined(HYPRE_MIXEDINT) && defined(PETSC_USE_64BIT_INDICES))
|
||||
#if (defined(HYPRE_BIGINT) && !defined(PETSC_USE_64BIT_INDICES)) || \
|
||||
(!defined(HYPRE_BIGINT) && defined(PETSC_USE_64BIT_INDICES))
|
||||
#error HYPRE and PETSC do not use the same size integers!
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+5
-48
@@ -211,24 +211,6 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
own_ParVector = 1;
|
||||
}
|
||||
|
||||
HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
Vector &base, int offset, HYPRE_BigInt *col)
|
||||
: HypreParVector(comm, glob_size, nullptr, col, false)
|
||||
{
|
||||
MFEM_ASSERT(CanShallowCopy(base.GetMemory(), GetHypreMemoryClass()),
|
||||
"the MemoryTypes of 'base' are incompatible with Hypre!");
|
||||
MFEM_ASSERT(offset + size <= base.Size(),
|
||||
"the size of 'base' is too small!");
|
||||
|
||||
data.Delete();
|
||||
data.MakeAlias(base.GetMemory(), offset, size);
|
||||
hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
|
||||
hypre_VectorData(x_loc) = data.ReadWrite(GetHypreMemoryClass(), size);
|
||||
#ifdef HYPRE_USING_GPU
|
||||
hypre_VectorMemoryLocation(x_loc) = GetHypreMemoryLocation();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Call the move constructor on the "compatible" temp vector
|
||||
HypreParVector::HypreParVector(const HypreParVector &y) : HypreParVector(
|
||||
y.CreateCompatibleVector())
|
||||
@@ -1598,12 +1580,14 @@ void HypreParMatrix::GetDiag(Vector &diag) const
|
||||
{
|
||||
const int size = Height();
|
||||
diag.SetSize(size);
|
||||
auto hypre_ml = GetHypreMemoryLocation();
|
||||
// Avoid using GetHypreMemoryClass() since it may be MemoryClass::MANAGED and
|
||||
// that may not play well with the memory types used by 'diag'.
|
||||
MemoryClass hypre_mc = GetHypreForallMemoryClass();
|
||||
MemoryClass hypre_mc = (hypre_ml == HYPRE_MEMORY_HOST) ?
|
||||
MemoryClass::HOST : MemoryClass::DEVICE;
|
||||
real_t *diag_hd = diag.GetMemory().Write(hypre_mc, size);
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
MFEM_VERIFY(A->diag->memory_location == GetHypreMemoryLocation(),
|
||||
MFEM_VERIFY(A->diag->memory_location == hypre_ml,
|
||||
"unexpected HypreParMatrix memory location!");
|
||||
#endif
|
||||
const HYPRE_Int *A_diag_i = A->diag->i;
|
||||
@@ -2510,7 +2494,7 @@ void HypreParMatrix::EliminateBC(const Array<int> &ess_dofs,
|
||||
|
||||
const int n_ess_dofs = ess_dofs.Size();
|
||||
const auto ess_dofs_d = ess_dofs.GetMemory().Read(
|
||||
GetHypreForallMemoryClass(), n_ess_dofs);
|
||||
GetHypreMemoryClass(), n_ess_dofs);
|
||||
|
||||
// Start communication to figure out which columns need to be eliminated in
|
||||
// the off-diagonal block
|
||||
@@ -2793,33 +2777,6 @@ void HypreParMatrix::PrintHash(std::ostream &os) const
|
||||
os << "col map offd hash : " << hf.GetHash() << '\n';
|
||||
}
|
||||
|
||||
real_t HypreParMatrix::FNorm() const
|
||||
{
|
||||
real_t norm_fro = 0.0;
|
||||
if (A != NULL)
|
||||
#if MFEM_HYPRE_VERSION >= 21900
|
||||
{
|
||||
const int ierr = hypre_ParCSRMatrixNormFro(A, &norm_fro);
|
||||
MFEM_VERIFY(ierr == 0, "");
|
||||
}
|
||||
#else
|
||||
{
|
||||
// HYPRE_USING_GPU is not defined for
|
||||
// MFEM_HYPRE_VERSION < 22100 and so here it is
|
||||
// guaranteed that the matrix is in "host" memory
|
||||
Vector Avec_diag(A->diag->data, A->diag->num_nonzeros);
|
||||
real_t normsqr_fro = InnerProduct(Avec_diag, Avec_diag);
|
||||
Vector Avec_offd(A->offd->data, A->offd->num_nonzeros);
|
||||
normsqr_fro += InnerProduct(Avec_offd, Avec_offd);
|
||||
MPI_Allreduce(MPI_IN_PLACE, &normsqr_fro, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, hypre_ParCSRMatrixComm(A));
|
||||
norm_fro = sqrt(normsqr_fro);
|
||||
}
|
||||
#endif
|
||||
return norm_fro;
|
||||
}
|
||||
|
||||
|
||||
inline void delete_hypre_ParCSRMatrixColMapOffd(hypre_ParCSRMatrix *A)
|
||||
{
|
||||
HYPRE_BigInt *A_col_map_offd = hypre_ParCSRMatrixColMapOffd(A);
|
||||
|
||||
+5
-16
@@ -247,12 +247,6 @@ public:
|
||||
allocated in the memory location HYPRE_MEMORY_DEVICE. */
|
||||
HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size, real_t *data_,
|
||||
HYPRE_BigInt *col, bool is_device_ptr = false);
|
||||
/** @brief Creates a vector that uses the data of the Vector @a base,
|
||||
starting at the given @a offset. */
|
||||
/** The @a base Vector must have memory types compatible with the MemoryClass
|
||||
returned by GetHypreMemoryClass(). */
|
||||
HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size, Vector &base,
|
||||
int offset, HYPRE_BigInt *col);
|
||||
/// Creates a deep copy of @a y
|
||||
HypreParVector(const HypreParVector &y);
|
||||
/// Move constructor for HypreParVector. "Steals" data from its argument.
|
||||
@@ -318,8 +312,7 @@ public:
|
||||
/// Sets the data of the Vector and the hypre_ParVector to @a data_.
|
||||
/** Must be used only for HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
*/
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *). */
|
||||
void SetData(real_t *data_);
|
||||
|
||||
/** @brief Prepare the HypreParVector for read access in hypre's device
|
||||
@@ -339,7 +332,7 @@ public:
|
||||
HYPRE_MEMORY_DEVICE. */
|
||||
/** This method must be used with HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *).
|
||||
|
||||
The Memory @a mem must be accessible with the hypre MemoryClass defined
|
||||
by GetHypreMemoryClass(). */
|
||||
@@ -350,7 +343,7 @@ public:
|
||||
space, HYPRE_MEMORY_DEVICE. */
|
||||
/** This method must be used with HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *).
|
||||
|
||||
The Memory @a mem must be accessible with the hypre MemoryClass defined
|
||||
by GetHypreMemoryClass(). */
|
||||
@@ -361,7 +354,7 @@ public:
|
||||
HYPRE_MEMORY_DEVICE. */
|
||||
/** This method must be used with HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *).
|
||||
|
||||
The Memory @a mem must be accessible with the hypre MemoryClass defined
|
||||
by GetHypreMemoryClass(). */
|
||||
@@ -400,7 +393,7 @@ private:
|
||||
/// Auxiliary vectors for typecasting
|
||||
mutable HypreParVector *X, *Y;
|
||||
/** @brief Auxiliary buffers for the case when the input or output arrays in
|
||||
methods like Mult(real_t, const Vector &, real_t, Vector &) need to be
|
||||
methods like Mult(double, const Vector &, double, Vector &) need to be
|
||||
deep copied in order to be used by hypre. */
|
||||
mutable Memory<real_t> auxX, auxY;
|
||||
|
||||
@@ -945,10 +938,6 @@ public:
|
||||
without the need to save the whole matrix. */
|
||||
void PrintHash(std::ostream &out) const;
|
||||
|
||||
/// @brief Return the Frobenius norm of the matrix (or 0 if the underlying
|
||||
/// hypre matrix is NULL)
|
||||
real_t FNorm() const;
|
||||
|
||||
/// Calls hypre's destroy function
|
||||
virtual ~HypreParMatrix() { Destroy(); }
|
||||
|
||||
|
||||
+14
-38
@@ -402,43 +402,6 @@ void MultABt(const int Aheight, const int Awidth, const int Bheight,
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Compute C = alpha*At*B + beta*C.
|
||||
|
||||
Multiply the transpose of a matrix of size @a Aheight x @a Awidth and data
|
||||
@a Adata with a matrix of size @a Aheight x @a Bwidth and data @a Bdata. */
|
||||
template<typename TA, typename TB, typename TC>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void AddMultAtB(const int Aheight, const int Awidth, const int Bwidth,
|
||||
const TA *Adata, const TB *Bdata, TC *Cdata, const TB alpha,
|
||||
const TA beta)
|
||||
{
|
||||
const int aw_x_bw = Awidth * Bwidth;
|
||||
|
||||
if (beta == 0.0)
|
||||
{
|
||||
for (int i = 0; i < aw_x_bw; i++) { Cdata[i] = 0.0; }
|
||||
}
|
||||
else if (beta != 1.0)
|
||||
{
|
||||
for (int i = 0; i < aw_x_bw; i++) { Cdata[i] *= beta; }
|
||||
}
|
||||
|
||||
TC *c = Cdata;
|
||||
for (int i = 0; i < Bwidth; ++i)
|
||||
{
|
||||
for (int j = 0; j < Awidth; ++j)
|
||||
{
|
||||
TC val = 0.0;
|
||||
for (int k = 0; k < Aheight; ++k)
|
||||
{
|
||||
val += alpha * Adata[j * Aheight + k] * Bdata[i * Aheight + k];
|
||||
}
|
||||
*c += val;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Multiply the transpose of a matrix of size @a Aheight x @a Awidth
|
||||
and data @a Adata with a matrix of size @a Aheight x @a Bwidth and data @a
|
||||
Bdata: At * B. Return the result in a matrix with data @a AtBdata. */
|
||||
@@ -447,7 +410,20 @@ MFEM_HOST_DEVICE inline
|
||||
void MultAtB(const int Aheight, const int Awidth, const int Bwidth,
|
||||
const TA *Adata, const TB *Bdata, TC *AtBdata)
|
||||
{
|
||||
AddMultAtB(Aheight, Awidth, Bwidth, Adata, Bdata, AtBdata, TB(1.0), TA(0.0));
|
||||
TC *c = AtBdata;
|
||||
for (int i = 0; i < Bwidth; ++i)
|
||||
{
|
||||
for (int j = 0; j < Awidth; ++j)
|
||||
{
|
||||
TC val = 0.0;
|
||||
for (int k = 0; k < Aheight; ++k)
|
||||
{
|
||||
val += Adata[j * Aheight + k] * Bdata[i * Aheight + k];
|
||||
}
|
||||
*c = val;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a matrix of size 2x1, 3x1, or 3x2, compute the left inverse.
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2010-2023, 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_MDVECTOR
|
||||
#define MFEM_MDVECTOR
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#include "vector.hpp"
|
||||
#include "general/mdspan.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int N, typename Layout = MDLayoutLeft<N>>
|
||||
struct MDVector : public MDSpan<Vector, N, Layout>
|
||||
{
|
||||
using base_t = MDSpan<Vector, N, Layout>;
|
||||
|
||||
/**
|
||||
* @brief MDVector default constructor (recursion)
|
||||
*/
|
||||
MDVector(): base_t() { }
|
||||
|
||||
/**
|
||||
* @brief MDVector recursion constructor
|
||||
* @param[in] n Dimension indice
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDVector(int n, Ts... args): MDVector(args...) { base_t::Setup(n, args...); }
|
||||
|
||||
/// Move constructor not supported
|
||||
MDVector(MDVector&&) = delete;
|
||||
|
||||
/// Copy constructor not supported
|
||||
MDVector(const MDVector&) = delete;
|
||||
|
||||
/// Move assignment not supported
|
||||
MDVector& operator=(MDVector&&) = delete;
|
||||
|
||||
/// Copy assignment not supported
|
||||
MDVector& operator=(const MDVector&) = delete;
|
||||
|
||||
using Vector::Read;
|
||||
using Vector::Write;
|
||||
using Vector::ReadWrite;
|
||||
using Vector::HostRead;
|
||||
using Vector::HostWrite;
|
||||
using Vector::HostReadWrite;
|
||||
|
||||
using Vector::GetData;
|
||||
using Vector::SetData;
|
||||
|
||||
using Vector::operator=;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDVECTOR
|
||||
+235
-315
@@ -9,155 +9,12 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../general/communication.hpp"
|
||||
#include "operator.hpp"
|
||||
#include "ode.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
std::string ODESolver::ExplicitTypes =
|
||||
"\n\tExplicit solver: \n\t"
|
||||
" RK : 1 - Forward Euler, 2 - RK2(0.5), 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
||||
" AB : 11 - AB1, 12 - AB2, 13 - AB3, 14 - AB4, 15 - AB5\n";
|
||||
|
||||
std::string ODESolver::ImplicitTypes =
|
||||
"\n\tImplicit solver: \n\t"
|
||||
" (L-Stab): 21 - Backward Euler, 22 - SDIRK23(2), 23 - SDIRK33,\n\t"
|
||||
" (A-Stab): 32 - Implicit Midpoint, 33 - SDIRK23, 34 - SDIRK34,\n\t"
|
||||
" GA : 40 -- 50 - Generalized-alpha,\n\t"
|
||||
" AM : 51 - AM1, 52 - AM2, 53 - AM3, 54 - AM4\n";
|
||||
|
||||
std::string ODESolver::Types = ODESolver::ExplicitTypes +
|
||||
ODESolver::ImplicitTypes;
|
||||
|
||||
std::unique_ptr<ODESolver> ODESolver::Select(int ode_solver_type)
|
||||
{
|
||||
if (ode_solver_type < 20)
|
||||
{
|
||||
return SelectExplicit(ode_solver_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SelectImplicit(ode_solver_type);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ODESolver> ODESolver::SelectExplicit(int ode_solver_type)
|
||||
{
|
||||
using ode_ptr = std::unique_ptr<ODESolver>;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit RK methods
|
||||
case 1: return ode_ptr(new ForwardEulerSolver);
|
||||
case 2: return ode_ptr(new RK2Solver(0.5)); // midpoint method
|
||||
case 3: return ode_ptr(new RK3SSPSolver);
|
||||
case 4: return ode_ptr(new RK4Solver);
|
||||
case 6: return ode_ptr(new RK6Solver);
|
||||
|
||||
// Explicit AB methods
|
||||
case 11: return ode_ptr(new AB1Solver);
|
||||
case 12: return ode_ptr(new AB2Solver);
|
||||
case 13: return ode_ptr(new AB3Solver);
|
||||
case 14: return ode_ptr(new AB4Solver);
|
||||
case 15: return ode_ptr(new AB5Solver);
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ODESolver> ODESolver::SelectImplicit(int ode_solver_type)
|
||||
{
|
||||
using ode_ptr = std::unique_ptr<ODESolver>;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 21: return ode_ptr(new BackwardEulerSolver);
|
||||
case 22: return ode_ptr(new SDIRK23Solver(2));
|
||||
case 23: return ode_ptr(new SDIRK33Solver);
|
||||
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 32: return ode_ptr(new ImplicitMidpointSolver);
|
||||
case 33: return ode_ptr(new SDIRK23Solver);
|
||||
case 34: return ode_ptr(new SDIRK34Solver);
|
||||
|
||||
// Implicit generalized alpha
|
||||
case 40: return ode_ptr(new GeneralizedAlphaSolver(0.0));
|
||||
case 41: return ode_ptr(new GeneralizedAlphaSolver(0.1));
|
||||
case 42: return ode_ptr(new GeneralizedAlphaSolver(0.2));
|
||||
case 43: return ode_ptr(new GeneralizedAlphaSolver(0.3));
|
||||
case 44: return ode_ptr(new GeneralizedAlphaSolver(0.4));
|
||||
case 45: return ode_ptr(new GeneralizedAlphaSolver(0.5));
|
||||
case 46: return ode_ptr(new GeneralizedAlphaSolver(0.6));
|
||||
case 47: return ode_ptr(new GeneralizedAlphaSolver(0.7));
|
||||
case 48: return ode_ptr(new GeneralizedAlphaSolver(0.8));
|
||||
case 49: return ode_ptr(new GeneralizedAlphaSolver(0.9));
|
||||
case 50: return ode_ptr(new GeneralizedAlphaSolver(1.0));
|
||||
|
||||
// Implicit AM methods
|
||||
case 51: return ode_ptr(new AM1Solver);
|
||||
case 52: return ode_ptr(new AM2Solver);
|
||||
case 53: return ode_ptr(new AM3Solver);
|
||||
case 54: return ode_ptr(new AM4Solver);
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODEStateDataVector::SetSize( int vsize, MemoryType m_t)
|
||||
{
|
||||
mem_type = m_t;
|
||||
for (int i = 0; i < smax; i++)
|
||||
{
|
||||
idx[i] = smax - i - 1;
|
||||
data[i].SetSize(vsize, mem_type);
|
||||
}
|
||||
|
||||
ss = 0;
|
||||
}
|
||||
|
||||
const Vector &ODEStateDataVector::Get(int i) const
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss);
|
||||
return data[idx[i]];
|
||||
}
|
||||
|
||||
Vector &ODEStateDataVector::Get(int i)
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss);
|
||||
return data[idx[i]];
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Get(int i, Vector &vec) const
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss);
|
||||
vec = data[idx[i]];
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Set(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,smax);
|
||||
data[idx[i]] = state;
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Append(Vector &state)
|
||||
{
|
||||
ShiftStages();
|
||||
data[idx[0]] = state;
|
||||
Increment();
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Print(std::ostream &os) const
|
||||
{
|
||||
os << ss <<"/" <<smax<<std::endl;
|
||||
idx.Print(os);
|
||||
for (int i = 0; i < ss; i++) { data[idx[i]].Print(os); }
|
||||
}
|
||||
|
||||
|
||||
void ODESolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
this->f = &f_;
|
||||
@@ -487,65 +344,104 @@ const real_t RK8Solver::c[] =
|
||||
};
|
||||
|
||||
|
||||
AdamsBashforthSolver::AdamsBashforthSolver(int s_, const real_t *a_):
|
||||
stages(s_), state(s_)
|
||||
AdamsBashforthSolver::AdamsBashforthSolver(int s_, const real_t *a_)
|
||||
{
|
||||
smax = std::min(s_,5);
|
||||
a = a_;
|
||||
k = new Vector[5];
|
||||
dt_ = -1.0;
|
||||
|
||||
if (smax <= 2)
|
||||
{
|
||||
RKsolver = new RK2Solver();
|
||||
}
|
||||
else if (smax == 3)
|
||||
{
|
||||
RKsolver = new RK3SSPSolver();
|
||||
}
|
||||
else
|
||||
{
|
||||
RKsolver = new RK4Solver();
|
||||
}
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsBashforthSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
|
||||
state = k[idx[i]];
|
||||
}
|
||||
|
||||
const Vector &AdamsBashforthSolver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsBashforthSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
|
||||
return k[idx[i]];
|
||||
}
|
||||
|
||||
|
||||
void AdamsBashforthSolver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < smax ),
|
||||
" AdamsBashforthSolver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
k[idx[i]] = state;
|
||||
s = std::max(i,s);
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
ODESolver::Init(f_);
|
||||
if (RKsolver) { RKsolver->Init(f_); }
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
dt_ = -1.0;
|
||||
RKsolver->Init(f_);
|
||||
idx.SetSize(smax);
|
||||
for (int i = 0; i < smax; i++)
|
||||
{
|
||||
idx[i] = (smax-i)%smax;
|
||||
k[i].SetSize(f->Width());
|
||||
}
|
||||
s = 0;
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
CheckTimestep(dt);
|
||||
|
||||
if (state.Size() >= stages -1)
|
||||
if ( (dt_ > 0.0) && (fabs(dt-dt_) >10*std::numeric_limits<real_t>::epsilon()))
|
||||
{
|
||||
f->SetTime(t);
|
||||
f->Mult(x, state[0]);
|
||||
state.Increment();
|
||||
for (int i = 0; i < stages; i++)
|
||||
{
|
||||
x.Add(a[i]*dt, state[i]);
|
||||
}
|
||||
t += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x,state[0]);
|
||||
RKsolver->Step(x,t,dt);
|
||||
state.Increment();
|
||||
}
|
||||
|
||||
state.ShiftStages();
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::CheckTimestep(real_t dt)
|
||||
{
|
||||
if (dt_ < 0.0)
|
||||
{
|
||||
dt_ = dt;
|
||||
return;
|
||||
}
|
||||
else if (fabs(dt-dt_) >10*std::numeric_limits<real_t>::epsilon())
|
||||
{
|
||||
state.Reset();
|
||||
s = 0;
|
||||
dt_ = dt;
|
||||
|
||||
if (print())
|
||||
{
|
||||
mfem::out << "WARNING:" << std::endl;
|
||||
mfem::out << " - Time step changed" << std::endl;
|
||||
mfem::out << " - Purging time stepping history" << std::endl;
|
||||
mfem::out << " - Purging Adams-Bashforth history" << std::endl;
|
||||
mfem::out << " - Will run Runge-Kutta to rebuild history" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
s++;
|
||||
s = std::min(s, smax);
|
||||
if (s == smax)
|
||||
{
|
||||
f->SetTime(t);
|
||||
f->Mult(x, k[idx[0]]);
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
x.Add(a[i]*dt, k[idx[i]]);
|
||||
}
|
||||
t += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x,k[idx[0]]);
|
||||
RKsolver->Step(x,t,dt);
|
||||
}
|
||||
|
||||
// Shift the index
|
||||
for (int i = 0; i < smax; i++) { idx[i] = ++idx[i]%smax; }
|
||||
}
|
||||
|
||||
const real_t AB1Solver::a[] =
|
||||
@@ -559,68 +455,110 @@ const real_t AB4Solver::a[] =
|
||||
const real_t AB5Solver::a[] =
|
||||
{1901.0/720.0,-2774.0/720.0, 2616.0/720.0,-1274.0/720.0, 251.0/720.0};
|
||||
|
||||
|
||||
AdamsMoultonSolver::AdamsMoultonSolver(int s_, const real_t *a_):
|
||||
stages(s_), state(s_)
|
||||
AdamsMoultonSolver::AdamsMoultonSolver(int s_, const real_t *a_)
|
||||
{
|
||||
s = 0;
|
||||
smax = std::min(s_+1,5);
|
||||
a = a_;
|
||||
k = new Vector[5];
|
||||
dt_ = -1.0;
|
||||
|
||||
if (smax <= 3)
|
||||
{
|
||||
RKsolver = new SDIRK23Solver();
|
||||
}
|
||||
else
|
||||
{
|
||||
RKsolver = new SDIRK34Solver();
|
||||
}
|
||||
}
|
||||
|
||||
const Vector &AdamsMoultonSolver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsMoultonSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
return k[idx[i+1]];
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsMoultonSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
state = k[idx[i+1]];
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < smax ),
|
||||
" AdamsMoultonSolver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
k[idx[i+1]] = state;
|
||||
s = std::max(i,s);
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
ODESolver::Init(f_);
|
||||
if (RKsolver) { RKsolver->Init(f_); }
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
dt_ = -1.0;
|
||||
RKsolver->Init(f_);
|
||||
int n = f->Width();
|
||||
idx.SetSize(smax);
|
||||
for (int i = 0; i < smax; i++)
|
||||
{
|
||||
idx[i] = (smax-i)%smax;
|
||||
k[i].SetSize(n);
|
||||
}
|
||||
s = 0;
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
if (dt_ < 0.0)
|
||||
if ( (dt_ > 0.0) && (fabs(dt-dt_) >10*std::numeric_limits<real_t>::epsilon()))
|
||||
{
|
||||
dt_ = dt;
|
||||
}
|
||||
else if (fabs(dt-dt_) > 10*std::numeric_limits<real_t>::epsilon())
|
||||
{
|
||||
state.Reset();
|
||||
s = 0;
|
||||
dt_ = dt;
|
||||
|
||||
if (print())
|
||||
{
|
||||
mfem::out << "WARNING:" << std::endl;
|
||||
mfem::out << " - Time step changed" << std::endl;
|
||||
mfem::out << " - Purging time stepping history" << std::endl;
|
||||
mfem::out << " - Purging Adams-Moulton history" << std::endl;
|
||||
mfem::out << " - Will run Runge-Kutta to rebuild history" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if ((state.Size() == 0)&&(stages>1))
|
||||
if ((s == 0)&&(smax>1))
|
||||
{
|
||||
f->Mult(x,state[0]);
|
||||
state.Increment();
|
||||
f->Mult(x,k[idx[1]]);
|
||||
}
|
||||
s++;
|
||||
s = std::min(s, smax);
|
||||
|
||||
if (state.Size() >= stages )
|
||||
if (s >= smax-1)
|
||||
{
|
||||
f->SetTime(t);
|
||||
for (int i = 0; i < stages; i++)
|
||||
for (int i = 1; i < smax; i++)
|
||||
{
|
||||
x.Add(a[i+1]*dt, state[i]);
|
||||
x.Add(a[i]*dt, k[idx[i]]);
|
||||
}
|
||||
state.ShiftStages();
|
||||
f->ImplicitSolve(a[0]*dt, x, state[0]);
|
||||
x.Add(a[0]*dt, state[0]);
|
||||
f->ImplicitSolve(a[0]*dt, x, k[idx[0]]);
|
||||
x.Add(a[0]*dt, k[idx[0]]);
|
||||
t += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ShiftStages();
|
||||
RKsolver->Step(x,t,dt);
|
||||
f->Mult(x,state[0]);
|
||||
state.Increment();
|
||||
f->Mult(x,k[idx[0]]);
|
||||
}
|
||||
|
||||
|
||||
// Shift the index
|
||||
for (int i = 0; i < smax; i++) { idx[i] = ++idx[i]%smax; }
|
||||
}
|
||||
|
||||
const real_t AM0Solver::a[] =
|
||||
{1.0};
|
||||
const real_t AM1Solver::a[] =
|
||||
{0.5, 0.5};
|
||||
const real_t AM2Solver::a[] =
|
||||
@@ -879,7 +817,34 @@ void GeneralizedAlphaSolver::Init(TimeDependentOperator &f_)
|
||||
ODESolver::Init(f_);
|
||||
k.SetSize(f->Width(), mem_type);
|
||||
y.SetSize(f->Width(), mem_type);
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
xdot.SetSize(f->Width(), mem_type);
|
||||
xdot = 0.0;
|
||||
nstate = 0;
|
||||
}
|
||||
|
||||
const Vector &GeneralizedAlphaSolver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlphaSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
return xdot;
|
||||
}
|
||||
|
||||
void GeneralizedAlphaSolver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlphaSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
state = xdot;
|
||||
}
|
||||
|
||||
void GeneralizedAlphaSolver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0),
|
||||
"GeneralizedAlphaSolver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
xdot = state;
|
||||
nstate = 1;
|
||||
}
|
||||
|
||||
void GeneralizedAlphaSolver::SetRhoInf(real_t rho_inf)
|
||||
@@ -919,17 +884,17 @@ void GeneralizedAlphaSolver::PrintProperties(std::ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
// This routine state[0] represents xdot
|
||||
// This routine assumes xdot is initialized.
|
||||
void GeneralizedAlphaSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
if (state.Size() == 0)
|
||||
if (nstate == 0)
|
||||
{
|
||||
f->Mult(x,state[0]);
|
||||
state.Increment();
|
||||
f->Mult(x,xdot);
|
||||
nstate = 1;
|
||||
}
|
||||
|
||||
// Set y = x + alpha_f*(1.0 - (gamma/alpha_m))*dt*xdot
|
||||
add(x, alpha_f*(1.0 - (gamma/alpha_m))*dt, state[0], y);
|
||||
add(x, alpha_f*(1.0 - (gamma/alpha_m))*dt, xdot, y);
|
||||
|
||||
// Solve k = f(y + dt_eff*k)
|
||||
real_t dt_eff = (gamma*alpha_f/alpha_m)*dt;
|
||||
@@ -937,11 +902,11 @@ void GeneralizedAlphaSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
f->ImplicitSolve(dt_eff, y, k);
|
||||
|
||||
// Update x and xdot
|
||||
x.Add((1.0 - (gamma/alpha_m))*dt, state[0]);
|
||||
x.Add((1.0 - (gamma/alpha_m))*dt, xdot);
|
||||
x.Add( (gamma/alpha_m) *dt, k);
|
||||
|
||||
state[0] *= (1.0-(1.0/alpha_m));
|
||||
state[0].Add((1.0/alpha_m),k);
|
||||
xdot *= (1.0-(1.0/alpha_m));
|
||||
xdot.Add((1.0/alpha_m),k);
|
||||
|
||||
t += dt;
|
||||
}
|
||||
@@ -1052,75 +1017,18 @@ SIAVSolver::Step(Vector &q, Vector &p, real_t &t, real_t &dt)
|
||||
}
|
||||
}
|
||||
|
||||
std::string SecondOrderODESolver::Types =
|
||||
"ODE solver: \n\t"
|
||||
" [0--10] - GeneralizedAlpha(0.1 * s),\n\t"
|
||||
" 11 - Average Acceleration, 12 - Linear Acceleration\n\t"
|
||||
" 13 - CentralDifference, 14 - FoxGoodwin";
|
||||
|
||||
SecondOrderODESolver* SecondOrderODESolver::Select(int ode_solver_type)
|
||||
{
|
||||
SecondOrderODESolver* ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit methods
|
||||
case 0: ode_solver = new GeneralizedAlpha2Solver(0.0); break;
|
||||
case 1: ode_solver = new GeneralizedAlpha2Solver(0.1); break;
|
||||
case 2: ode_solver = new GeneralizedAlpha2Solver(0.2); break;
|
||||
case 3: ode_solver = new GeneralizedAlpha2Solver(0.3); break;
|
||||
case 4: ode_solver = new GeneralizedAlpha2Solver(0.4); break;
|
||||
case 5: ode_solver = new GeneralizedAlpha2Solver(0.5); break;
|
||||
case 6: ode_solver = new GeneralizedAlpha2Solver(0.6); break;
|
||||
case 7: ode_solver = new GeneralizedAlpha2Solver(0.7); break;
|
||||
case 8: ode_solver = new GeneralizedAlpha2Solver(0.8); break;
|
||||
case 9: ode_solver = new GeneralizedAlpha2Solver(0.9); break;
|
||||
case 10: ode_solver = new GeneralizedAlpha2Solver(1.0); break;
|
||||
|
||||
case 11: ode_solver = new AverageAccelerationSolver(); break;
|
||||
case 12: ode_solver = new LinearAccelerationSolver(); break;
|
||||
case 13: ode_solver = new CentralDifferenceSolver(); break;
|
||||
case 14: ode_solver = new FoxGoodwinSolver(); break;
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type);
|
||||
}
|
||||
return ode_solver;
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void SecondOrderODESolver::EulerStep(Vector &x, Vector &dxdt, real_t &t,
|
||||
real_t &dt)
|
||||
{
|
||||
x.Add(dt, dxdt);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(0.5*dt*dt, dt, x, dxdt, state[0]);
|
||||
|
||||
x .Add(0.5*dt*dt, state[0]);
|
||||
dxdt.Add(dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void SecondOrderODESolver::MidPointStep(Vector &x, Vector &dxdt, real_t &t,
|
||||
real_t &dt)
|
||||
{
|
||||
x.Add(0.5*dt, dxdt);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(0.25*dt*dt, 0.5*dt, x, dxdt, state[0]);
|
||||
|
||||
x.Add(0.5*dt, dxdt);
|
||||
x.Add(0.5*dt*dt, state[0]);
|
||||
dxdt.Add(dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
void SecondOrderODESolver::Init(SecondOrderTimeDependentOperator &f_)
|
||||
{
|
||||
this->f = &f_;
|
||||
mem_type = GetMemoryType(f_.GetMemoryClass());
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
}
|
||||
|
||||
void NewmarkSolver::Init(SecondOrderTimeDependentOperator &f_)
|
||||
{
|
||||
SecondOrderODESolver::Init(f_);
|
||||
d2xdt2.SetSize(f->Width());
|
||||
d2xdt2 = 0.0;
|
||||
first = true;
|
||||
}
|
||||
|
||||
void NewmarkSolver::PrintProperties(std::ostream &os)
|
||||
@@ -1152,7 +1060,6 @@ void NewmarkSolver::PrintProperties(std::ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void NewmarkSolver::Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt)
|
||||
{
|
||||
real_t fac0 = 0.5 - beta;
|
||||
@@ -1161,38 +1068,60 @@ void NewmarkSolver::Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt)
|
||||
real_t fac4 = gamma;
|
||||
|
||||
// In the first pass compute d2xdt2 directly from operator.
|
||||
if (state.Size() == 0)
|
||||
if (first)
|
||||
{
|
||||
if (no_mult)
|
||||
{
|
||||
MidPointStep(x, dxdt, t, dt);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x, dxdt, state[0]);
|
||||
}
|
||||
f->Mult(x, dxdt, d2xdt2);
|
||||
first = false;
|
||||
}
|
||||
f->SetTime(t + dt);
|
||||
|
||||
x.Add(dt, dxdt);
|
||||
x.Add(fac0*dt*dt, state[0]);
|
||||
dxdt.Add(fac2*dt, state[0]);
|
||||
x.Add(fac0*dt*dt, d2xdt2);
|
||||
dxdt.Add(fac2*dt, d2xdt2);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, state[0]);
|
||||
f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, d2xdt2);
|
||||
|
||||
x .Add(fac3*dt*dt, state[0]);
|
||||
dxdt.Add(fac4*dt, state[0]);
|
||||
x .Add(fac3*dt*dt, d2xdt2);
|
||||
dxdt.Add(fac4*dt, d2xdt2);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
void GeneralizedAlpha2Solver::Init(SecondOrderTimeDependentOperator &f_)
|
||||
{
|
||||
SecondOrderODESolver::Init(f_);
|
||||
xa.SetSize(f->Width(), mem_type);
|
||||
va.SetSize(f->Width(), mem_type);
|
||||
aa.SetSize(f->Width(), mem_type);
|
||||
xa.SetSize(f->Width());
|
||||
va.SetSize(f->Width());
|
||||
aa.SetSize(f->Width());
|
||||
d2xdt2.SetSize(f->Width());
|
||||
d2xdt2 = 0.0;
|
||||
nstate = 0;
|
||||
}
|
||||
|
||||
const Vector &GeneralizedAlpha2Solver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlpha2Solver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
return d2xdt2;
|
||||
}
|
||||
|
||||
|
||||
void GeneralizedAlpha2Solver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlpha2Solver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
state = d2xdt2;
|
||||
}
|
||||
|
||||
void GeneralizedAlpha2Solver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0),
|
||||
"GeneralizedAlpha2Solver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
d2xdt2 = state;
|
||||
nstate = 1;
|
||||
}
|
||||
|
||||
void GeneralizedAlpha2Solver::PrintProperties(std::ostream &os)
|
||||
@@ -1224,7 +1153,6 @@ void GeneralizedAlpha2Solver::PrintProperties(std::ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void GeneralizedAlpha2Solver::Step(Vector &x, Vector &dxdt,
|
||||
real_t &t, real_t &dt)
|
||||
{
|
||||
@@ -1236,24 +1164,16 @@ void GeneralizedAlpha2Solver::Step(Vector &x, Vector &dxdt,
|
||||
real_t fac5 = alpha_m;
|
||||
|
||||
// In the first pass compute d2xdt2 directly from operator.
|
||||
if (state.Size() == 0)
|
||||
if (nstate == 0)
|
||||
{
|
||||
if (no_mult)
|
||||
{
|
||||
MidPointStep(x, dxdt, t, dt);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x, dxdt, state[0]);
|
||||
}
|
||||
state.Increment();
|
||||
f->Mult(x, dxdt, d2xdt2);
|
||||
nstate = 1;
|
||||
}
|
||||
|
||||
// Predict alpha levels
|
||||
add(dxdt, fac0*dt, state[0], va);
|
||||
add(dxdt, fac0*dt, d2xdt2, va);
|
||||
add(x, fac1*dt, va, xa);
|
||||
add(dxdt, fac2*dt, state[0], va);
|
||||
add(dxdt, fac2*dt, d2xdt2, va);
|
||||
|
||||
// Solve alpha levels
|
||||
f->SetTime(t + dt);
|
||||
@@ -1270,8 +1190,8 @@ void GeneralizedAlpha2Solver::Step(Vector &x, Vector &dxdt,
|
||||
dxdt *= 1.0 - 1.0/fac1;
|
||||
dxdt.Add (1.0/fac1, va);
|
||||
|
||||
state[0] *= 1.0 - 1.0/fac5;
|
||||
state[0].Add (1.0/fac5, aa);
|
||||
d2xdt2 *= 1.0 - 1.0/fac5;
|
||||
d2xdt2.Add (1.0/fac5, aa);
|
||||
|
||||
t += dt;
|
||||
}
|
||||
|
||||
+246
-319
@@ -12,99 +12,13 @@
|
||||
#ifndef MFEM_ODE
|
||||
#define MFEM_ODE
|
||||
|
||||
#include "../general/communication.hpp"
|
||||
#include "../config/config.hpp"
|
||||
#include "operator.hpp"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "../general/communication.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// An interface for storing state of previous timesteps
|
||||
class ODEStateData
|
||||
{
|
||||
public:
|
||||
/// Get the maximum number of stored stages
|
||||
virtual int MaxSize() const = 0;
|
||||
|
||||
/// Get the current number of stored stages
|
||||
virtual int Size() const = 0;
|
||||
|
||||
/// Get the ith state vector
|
||||
virtual const Vector &Get(int i) const = 0;
|
||||
|
||||
/// Get the ith state vector - non-const version
|
||||
virtual Vector &Get(int i) = 0;
|
||||
|
||||
/// Get the ith state vector - with a copy
|
||||
virtual void Get(int i, Vector &vec) const = 0;
|
||||
|
||||
/// Set the ith state vector
|
||||
virtual void Set(int i, Vector &state) = 0;
|
||||
|
||||
/// Add state vector and increment state size
|
||||
virtual void Append(Vector &state) = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~ODEStateData() = default;
|
||||
};
|
||||
|
||||
/// An implementation of ODEStateData that stores states in an std::vector<Vector>
|
||||
class ODEStateDataVector : public ODEStateData
|
||||
{
|
||||
private:
|
||||
MemoryType mem_type;
|
||||
int ss, smax;
|
||||
std::vector<Vector> data;
|
||||
Array<int> idx;
|
||||
|
||||
public:
|
||||
ODEStateDataVector (int smax): smax(smax)
|
||||
{
|
||||
data.resize(smax);
|
||||
idx.SetSize(smax);
|
||||
ss = 0;
|
||||
};
|
||||
|
||||
/// Set the number of stages and the size of the vectors
|
||||
void SetSize(int vsize, MemoryType mem_type);
|
||||
|
||||
/// Shift the stage counter for the next timestep
|
||||
inline void ShiftStages()
|
||||
{
|
||||
for (int i = 0; i < smax; i++) { idx[i] = (++idx[i])%smax; }
|
||||
};
|
||||
|
||||
/// Increment the stage counter
|
||||
void Increment() { ss++; ss = std::min(ss,smax); };
|
||||
|
||||
/// Reset the stage counter
|
||||
void Reset() { ss = 0; };
|
||||
|
||||
/// Reference access to the ith vector.
|
||||
inline Vector & operator[](int i) { return data[idx[i]]; };
|
||||
|
||||
/// Const reference access to the ith vector.
|
||||
inline const Vector &operator[](int i) const { return data[idx[i]]; };
|
||||
|
||||
/// Print state data
|
||||
void Print(std::ostream &os = mfem::out) const ;
|
||||
|
||||
int MaxSize() const override { return smax; };
|
||||
|
||||
int Size() const override { return ss; };
|
||||
|
||||
const Vector &Get(int i) const override;
|
||||
Vector &Get(int i) override;
|
||||
void Get(int i, Vector &vec) const override;
|
||||
|
||||
void Set(int i, Vector &state) override;
|
||||
|
||||
void Append(Vector &state) override;
|
||||
};
|
||||
|
||||
|
||||
/// Abstract class for solving systems of ODEs: dx/dt = f(x,t)
|
||||
class ODESolver
|
||||
{
|
||||
@@ -178,48 +92,26 @@ public:
|
||||
while (t < tf) { Step(x, t, dt); }
|
||||
}
|
||||
|
||||
/// Returns how many State vectors the ODE requires
|
||||
virtual int GetStateSize() { return 0; };
|
||||
|
||||
// Help info for ODESolver options
|
||||
static MFEM_EXPORT std::string ExplicitTypes;
|
||||
static MFEM_EXPORT std::string ImplicitTypes;
|
||||
static MFEM_EXPORT std::string Types;
|
||||
|
||||
/// Function for selecting the desired ODESolver (Explicit and Implicit)
|
||||
/// Returns an ODESolver pointer based on an type
|
||||
/// Caller gets ownership of the object and is responsible for its deletion
|
||||
static MFEM_EXPORT std::unique_ptr<ODESolver> Select(const int ode_solver_type);
|
||||
|
||||
/// Function for selecting the desired Explicit ODESolver
|
||||
/// Returns an ODESolver pointer based on an type
|
||||
/// Caller gets ownership of the object and is responsible for its deletion
|
||||
static MFEM_EXPORT std::unique_ptr<ODESolver> SelectExplicit(
|
||||
const int ode_solver_type);
|
||||
|
||||
/// Function for selecting the desired Implicit ODESolver
|
||||
/// Returns an ODESolver pointer based on an type
|
||||
/// Caller gets ownership of the object and is responsible for its deletion
|
||||
static MFEM_EXPORT std::unique_ptr<ODESolver> SelectImplicit(
|
||||
const int ode_solver_type);
|
||||
/// Function for getting and setting the state vectors
|
||||
virtual int GetMaxStateSize() { return 0; }
|
||||
virtual int GetStateSize() { return 0; }
|
||||
virtual const Vector &GetStateVector(int i)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
Vector *s = NULL; return *s; // Make some compiler happy
|
||||
}
|
||||
virtual void GetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
virtual void SetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
|
||||
virtual ~ODESolver() { }
|
||||
};
|
||||
|
||||
/// Abstract class for an ODESolver that has state history implemented as ODEStateData
|
||||
class ODESolverWithStates : public ODESolver
|
||||
{
|
||||
public:
|
||||
/// Returns the StateData
|
||||
virtual ODEStateData& GetState() = 0;
|
||||
|
||||
/// Returns the StateData
|
||||
virtual const ODEStateData& GetState() const = 0;
|
||||
|
||||
/// Returns how many State vectors the ODE requires
|
||||
virtual int GetStateSize() { return GetState().MaxSize(); };
|
||||
};
|
||||
|
||||
|
||||
/// The classical forward Euler method
|
||||
class ForwardEulerSolver : public ODESolver
|
||||
@@ -325,13 +217,196 @@ public:
|
||||
class RK8Solver : public ExplicitRKSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[66], b[12], c[11];
|
||||
static const real_t a[66], b[12], c[11];
|
||||
|
||||
public:
|
||||
RK8Solver() : ExplicitRKSolver(12, a, b, c) { }
|
||||
};
|
||||
|
||||
|
||||
/** An explicit Adams-Bashforth method. */
|
||||
class AdamsBashforthSolver : public ODESolver
|
||||
{
|
||||
private:
|
||||
int s, smax;
|
||||
const real_t *a;
|
||||
Vector *k;
|
||||
Array<int> idx;
|
||||
ODESolver *RKsolver;
|
||||
real_t dt_;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
AdamsBashforthSolver(int s_, const real_t *a_);
|
||||
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return smax; };
|
||||
int GetStateSize() override { return s; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
|
||||
~AdamsBashforthSolver()
|
||||
{
|
||||
if (RKsolver) { delete RKsolver; }
|
||||
delete [] k;
|
||||
}
|
||||
};
|
||||
|
||||
/** A 1-stage, 1st order AB method. */
|
||||
class AB1Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[1];
|
||||
|
||||
public:
|
||||
AB1Solver() : AdamsBashforthSolver(1, a) { }
|
||||
};
|
||||
|
||||
/** A 2-stage, 2nd order AB method. */
|
||||
class AB2Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AB2Solver() : AdamsBashforthSolver(2, a) { }
|
||||
};
|
||||
|
||||
/** A 3-stage, 3rd order AB method. */
|
||||
class AB3Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AB3Solver() : AdamsBashforthSolver(3, a) { }
|
||||
};
|
||||
|
||||
/** A 4-stage, 4th order AB method. */
|
||||
class AB4Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AB4Solver() : AdamsBashforthSolver(4, a) { }
|
||||
};
|
||||
|
||||
/** A 5-stage, 5th order AB method. */
|
||||
class AB5Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AB5Solver() : AdamsBashforthSolver(5, a) { }
|
||||
};
|
||||
|
||||
|
||||
/** An implicit Adams-Moulton method. */
|
||||
class AdamsMoultonSolver : public ODESolver
|
||||
{
|
||||
private:
|
||||
int s, smax;
|
||||
const real_t *a;
|
||||
Vector *k;
|
||||
Array<int> idx;
|
||||
ODESolver *RKsolver;
|
||||
real_t dt_;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
AdamsMoultonSolver(int s_, const real_t *a_);
|
||||
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return smax-1; };
|
||||
int GetStateSize() override { return s-1; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
|
||||
~AdamsMoultonSolver()
|
||||
{
|
||||
if (RKsolver) { delete RKsolver; }
|
||||
delete [] k;
|
||||
};
|
||||
};
|
||||
|
||||
/** A 0-stage, 1st order AM method. */
|
||||
class AM0Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[1];
|
||||
|
||||
public:
|
||||
AM0Solver() : AdamsMoultonSolver(0, a) { }
|
||||
};
|
||||
|
||||
|
||||
/** A 1-stage, 2nd order AM method. */
|
||||
class AM1Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AM1Solver() : AdamsMoultonSolver(1, a) { }
|
||||
};
|
||||
|
||||
/** A 2-stage, 3rd order AM method. */
|
||||
class AM2Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AM2Solver() : AdamsMoultonSolver(2, a) { }
|
||||
};
|
||||
|
||||
/** A 3-stage, 4th order AM method. */
|
||||
class AM3Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AM3Solver() : AdamsMoultonSolver(3, a) { }
|
||||
};
|
||||
|
||||
/** A 4-stage, 5th order AM method. */
|
||||
class AM4Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AM4Solver() : AdamsMoultonSolver(4, a) { }
|
||||
};
|
||||
|
||||
|
||||
/// Backward Euler ODE solver. L-stable.
|
||||
class BackwardEulerSolver : public ODESolver
|
||||
{
|
||||
@@ -452,183 +527,31 @@ public:
|
||||
/// Generalized-alpha ODE solver from "A generalized-α method for integrating
|
||||
/// the filtered Navier-Stokes equations with a stabilized finite element
|
||||
/// method" by K.E. Jansen, C.H. Whiting and G.M. Hulbert.
|
||||
class GeneralizedAlphaSolver : public ODESolverWithStates
|
||||
class GeneralizedAlphaSolver : public ODESolver
|
||||
{
|
||||
ODEStateDataVector state;
|
||||
|
||||
protected:
|
||||
|
||||
mutable Vector k,y;
|
||||
mutable Vector xdot,k,y;
|
||||
real_t alpha_f, alpha_m, gamma;
|
||||
int nstate;
|
||||
|
||||
void SetRhoInf(real_t rho_inf);
|
||||
void PrintProperties(std::ostream &os = mfem::out);
|
||||
void PrintProperties(std::ostream &out = mfem::out);
|
||||
public:
|
||||
|
||||
GeneralizedAlphaSolver(real_t rho = 1.0) : state(1) { SetRhoInf(rho); };
|
||||
GeneralizedAlphaSolver(real_t rho = 1.0) { SetRhoInf(rho); };
|
||||
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
int GetMaxStateSize() override { return 1; };
|
||||
int GetStateSize() override { return nstate; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
};
|
||||
|
||||
|
||||
/** An explicit Adams-Bashforth method. */
|
||||
class AdamsBashforthSolver : public ODESolverWithStates
|
||||
{
|
||||
private:
|
||||
const real_t *a;
|
||||
const int stages;
|
||||
real_t dt_;
|
||||
ODEStateDataVector state;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ODESolver> RKsolver;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckTimestep(real_t dt);
|
||||
|
||||
public:
|
||||
AdamsBashforthSolver(int s_, const real_t *a_);
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
};
|
||||
|
||||
/** A 1-stage, 1st order AB method. */
|
||||
class AB1Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[1];
|
||||
|
||||
public:
|
||||
AB1Solver() : AdamsBashforthSolver(1, a) { }
|
||||
};
|
||||
|
||||
/** A 2-stage, 2nd order AB method. */
|
||||
class AB2Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AB2Solver() : AdamsBashforthSolver(2, a) { RKsolver.reset(new RK2Solver()); }
|
||||
};
|
||||
|
||||
/** A 3-stage, 3rd order AB method. */
|
||||
class AB3Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AB3Solver() : AdamsBashforthSolver(3, a) { RKsolver.reset(new RK3SSPSolver()); }
|
||||
};
|
||||
|
||||
/** A 4-stage, 4th order AB method. */
|
||||
class AB4Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AB4Solver() : AdamsBashforthSolver(4, a) { RKsolver.reset(new RK4Solver()); }
|
||||
};
|
||||
|
||||
/** A 5-stage, 5th order AB method. */
|
||||
class AB5Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AB5Solver() : AdamsBashforthSolver(5, a) { RKsolver.reset(new RK6Solver()); }
|
||||
};
|
||||
|
||||
|
||||
/** An implicit Adams-Moulton method. */
|
||||
class AdamsMoultonSolver : public ODESolverWithStates
|
||||
{
|
||||
private:
|
||||
const real_t *a;
|
||||
const int stages;
|
||||
real_t dt_;
|
||||
ODEStateDataVector state;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ODESolver> RKsolver;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckTimestep(real_t dt);
|
||||
|
||||
public:
|
||||
AdamsMoultonSolver(int s_, const real_t *a_);
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
};
|
||||
|
||||
/** A 1-stage, 2nd order AM method. */
|
||||
class AM1Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AM1Solver() : AdamsMoultonSolver(1, a) { RKsolver.reset(new SDIRK23Solver()); }
|
||||
};
|
||||
|
||||
/** A 2-stage, 3rd order AM method. */
|
||||
class AM2Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AM2Solver() : AdamsMoultonSolver(2, a) { RKsolver.reset(new SDIRK23Solver()); }
|
||||
};
|
||||
|
||||
/** A 3-stage, 4th order AM method. */
|
||||
class AM3Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AM3Solver() : AdamsMoultonSolver(3, a) { RKsolver.reset(new SDIRK23Solver()); }
|
||||
};
|
||||
|
||||
/** A 4-stage, 5th order AM method. */
|
||||
class AM4Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AM4Solver() : AdamsMoultonSolver(4, a) { RKsolver.reset(new SDIRK34Solver()); }
|
||||
};
|
||||
|
||||
/// The SIASolver class is based on the Symplectic Integration Algorithm
|
||||
/// described in "A Symplectic Integration Algorithm for Separable Hamiltonian
|
||||
/// Functions" by J. Candy and W. Rozmus, Journal of Computational Physics,
|
||||
@@ -707,10 +630,9 @@ protected:
|
||||
/// Pointer to the associated TimeDependentOperator.
|
||||
SecondOrderTimeDependentOperator *f; // f(.,.,t) : R^n x R^n --> R^n
|
||||
MemoryType mem_type;
|
||||
ODEStateDataVector state;
|
||||
|
||||
public:
|
||||
SecondOrderODESolver() : f(NULL), state(1) { mem_type = MemoryType::HOST; }
|
||||
SecondOrderODESolver() : f(NULL) { mem_type = MemoryType::HOST; }
|
||||
|
||||
/// Associate a TimeDependentOperator with the ODE solver.
|
||||
/** This method has to be called:
|
||||
@@ -758,8 +680,6 @@ public:
|
||||
sequence, then the ODE solver must be re-initialized by calling Init()
|
||||
between the two Step() calls. */
|
||||
virtual void Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt) = 0;
|
||||
void EulerStep(Vector &x, Vector &dxdt, real_t &t, real_t &dt);
|
||||
void MidPointStep(Vector &x, Vector &dxdt, real_t &t, real_t &dt);
|
||||
|
||||
/// Perform time integration from time @a t [in] to time @a tf [in].
|
||||
/** @param[in,out] x Approximate solution.
|
||||
@@ -785,18 +705,22 @@ public:
|
||||
while (t < tf) { Step(x, dxdt, t, dt); }
|
||||
}
|
||||
|
||||
/// Functions for getting the state vectors
|
||||
ODEStateData& GetState() { return state; }
|
||||
const ODEStateData& GetState() const { return state; }
|
||||
|
||||
/// Returns how many State vectors the ODE requires
|
||||
int GetStateSize() { return GetState().MaxSize(); };
|
||||
|
||||
/// Help info for SecondOrderODESolver options
|
||||
static MFEM_EXPORT std::string Types;
|
||||
|
||||
/// Function selecting the desired SecondOrderODESolver
|
||||
static MFEM_EXPORT SecondOrderODESolver *Select(const int ode_solver_type);
|
||||
/// Function for getting and setting the state vectors
|
||||
virtual int GetMaxStateSize() { return 0; };
|
||||
virtual int GetStateSize() { return 0; }
|
||||
virtual const Vector &GetStateVector(int i)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
Vector *s = NULL; return *s; // Make some compiler happy
|
||||
}
|
||||
virtual void GetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
virtual void SetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
|
||||
virtual ~SecondOrderODESolver() { }
|
||||
};
|
||||
@@ -807,18 +731,17 @@ public:
|
||||
class NewmarkSolver : public SecondOrderODESolver
|
||||
{
|
||||
private:
|
||||
Vector d2xdt2;
|
||||
|
||||
real_t beta, gamma;
|
||||
bool no_mult;
|
||||
bool first;
|
||||
|
||||
public:
|
||||
NewmarkSolver(real_t beta_ = 0.25, real_t gamma_ = 0.5, bool no_mult_ = false)
|
||||
{
|
||||
beta = beta_;
|
||||
gamma = gamma_;
|
||||
no_mult = no_mult_;
|
||||
};
|
||||
NewmarkSolver(real_t beta_ = 0.25, real_t gamma_ = 0.5) { beta = beta_; gamma = gamma_; };
|
||||
|
||||
void PrintProperties(std::ostream &os = mfem::out);
|
||||
void PrintProperties(std::ostream &out = mfem::out);
|
||||
|
||||
void Init(SecondOrderTimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt) override;
|
||||
};
|
||||
@@ -850,14 +773,13 @@ public:
|
||||
class GeneralizedAlpha2Solver : public SecondOrderODESolver
|
||||
{
|
||||
protected:
|
||||
Vector xa,va,aa;
|
||||
Vector xa,va,aa,d2xdt2;
|
||||
real_t alpha_f, alpha_m, beta, gamma;
|
||||
bool no_mult;
|
||||
int nstate;
|
||||
|
||||
public:
|
||||
GeneralizedAlpha2Solver(real_t rho_inf = 1.0, bool no_mult_ = false)
|
||||
GeneralizedAlpha2Solver(real_t rho_inf = 1.0)
|
||||
{
|
||||
no_mult = no_mult_;
|
||||
rho_inf = (rho_inf > 1.0) ? 1.0 : rho_inf;
|
||||
rho_inf = (rho_inf < 0.0) ? 0.0 : rho_inf;
|
||||
|
||||
@@ -867,12 +789,17 @@ public:
|
||||
gamma = 0.5 + alpha_m - alpha_f;
|
||||
};
|
||||
|
||||
void PrintProperties(std::ostream &os = mfem::out);
|
||||
void PrintProperties(std::ostream &out = mfem::out);
|
||||
|
||||
void Init(SecondOrderTimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return 1; };
|
||||
int GetStateSize() override { return nstate; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
};
|
||||
|
||||
/// The classical midpoint method.
|
||||
|
||||
+2
-2
@@ -36,10 +36,10 @@
|
||||
#if defined(PETSC_USE_COMPLEX)
|
||||
#error "MFEM does not work with PETSc compiled with complex numbers support"
|
||||
#endif
|
||||
#if defined(PETSC_USE_64BIT_INDICES) && !defined(HYPRE_BIGINT) && !defined(HYPRE_MIXEDINT)
|
||||
#if defined(PETSC_USE_64BIT_INDICES) && !defined(HYPRE_BIGINT)
|
||||
#error "Mismatch between HYPRE (32bit) and PETSc (64bit) integer types"
|
||||
#endif
|
||||
#if !defined(PETSC_USE_64BIT_INDICES) && (defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
|
||||
#if !defined(PETSC_USE_64BIT_INDICES) && defined(HYPRE_BIGINT)
|
||||
#error "Mismatch between HYPRE (64bit) and PETSc (32bit) integer types"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -574,6 +574,7 @@ void SLISolver::Mult(const Vector &b, Vector &x) const
|
||||
{
|
||||
oper->Mult(x, r);
|
||||
subtract(b, r, r); // r = b - A x
|
||||
Monitor(0,r.Norml2(),r,x,false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -623,6 +624,7 @@ void SLISolver::Mult(const Vector &b, Vector &x) const
|
||||
|
||||
oper->Mult(x, r);
|
||||
subtract(b, r, r); // r = b - A x
|
||||
Monitor(i,nom,r,x,false);
|
||||
|
||||
if (prec)
|
||||
{
|
||||
@@ -634,6 +636,8 @@ void SLISolver::Mult(const Vector &b, Vector &x) const
|
||||
nom = sqrt(Dot(r, r));
|
||||
}
|
||||
|
||||
|
||||
|
||||
cf = nom/nomold;
|
||||
nomold = nom;
|
||||
|
||||
|
||||
+79
-132
@@ -95,7 +95,7 @@ MFEM_DEPRECATED void* CVodeCreate(int lmm, SUNContext)
|
||||
|
||||
/// (DEPRECATED) Wrapper function for backwards compatibility with SUNDIALS
|
||||
/// version < 6
|
||||
MFEM_DEPRECATED void* ARKStepCreate(ARKRhsFn fe, ARKRhsFn fi, sunrealtype t0,
|
||||
MFEM_DEPRECATED void* ARKStepCreate(ARKRhsFn fe, ARKRhsFn fi, realtype t0,
|
||||
N_Vector y0, SUNContext)
|
||||
{
|
||||
return ARKStepCreate(fe, fi, t0, y0);
|
||||
@@ -127,7 +127,7 @@ MFEM_DEPRECATED N_Vector N_VNewEmpty_Parallel(MPI_Comm comm,
|
||||
/// (DEPRECATED) Wrapper function for backwards compatibility with SUNDIALS
|
||||
/// version < 6
|
||||
MFEM_DEPRECATED N_Vector SUN_Hip_OR_Cuda(N_VNewWithMemHelp)(sunindextype length,
|
||||
sunbooleantype use_managed_mem,
|
||||
booleantype use_managed_mem,
|
||||
SUNMemoryHelper helper,
|
||||
SUNContext)
|
||||
{
|
||||
@@ -157,16 +157,6 @@ MFEM_DEPRECATED N_Vector N_VMake_MPIPlusX(MPI_Comm comm, N_Vector local_vector,
|
||||
|
||||
#endif // SUNDIALS_VERSION_MAJOR < 6
|
||||
|
||||
#if MFEM_SUNDIALS_VERSION < 70100
|
||||
#define MFEM_ARKode(FUNC) ARKStep##FUNC
|
||||
#else
|
||||
#define MFEM_ARKode(FUNC) ARKode##FUNC
|
||||
#endif
|
||||
|
||||
// Macro STR(): expand the argument and add double quotes
|
||||
#define STR1(s) #s
|
||||
#define STR(s) STR1(s)
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -197,21 +187,11 @@ SundialsMemHelper &Sundials::GetMemHelper()
|
||||
Sundials::Sundials()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
int mpi_initialized = 0;
|
||||
MPI_Initialized(&mpi_initialized);
|
||||
MPI_Comm communicator = mpi_initialized ? MPI_COMM_WORLD : MPI_COMM_NULL;
|
||||
#if SUNDIALS_VERSION_MAJOR < 7
|
||||
MPI_Comm communicator = MPI_COMM_WORLD;
|
||||
int return_val = SUNContext_Create((void*) &communicator, &context);
|
||||
#else
|
||||
int return_val = SUNContext_Create(communicator, &context);
|
||||
#endif
|
||||
#else // #ifdef MFEM_USE_MPI
|
||||
#if SUNDIALS_VERSION_MAJOR < 7
|
||||
int return_val = SUNContext_Create(nullptr, &context);
|
||||
#else
|
||||
int return_val = SUNContext_Create((SUNComm)(0), &context);
|
||||
#endif
|
||||
#endif // #ifdef MFEM_USE_MPI
|
||||
MFEM_VERIFY(return_val == 0, "Call to SUNContext_Create failed");
|
||||
SundialsMemHelper actual_helper(context);
|
||||
memHelper = std::move(actual_helper);
|
||||
@@ -270,11 +250,7 @@ int SundialsMemHelper::SundialsMemHelper_Alloc(SUNMemoryHelper helper,
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if (SUNDIALS_VERSION_MAJOR < 7)
|
||||
SUNMemory sunmem = SUNMemoryNewEmpty();
|
||||
#else
|
||||
SUNMemory sunmem = SUNMemoryNewEmpty(helper->sunctx);
|
||||
#endif
|
||||
|
||||
sunmem->ptr = NULL;
|
||||
sunmem->own = SUNTRUE;
|
||||
@@ -655,7 +631,7 @@ static int LSFree(SUNLinearSolver LS)
|
||||
// ---------------------------------------------------------------------------
|
||||
// CVODE interface
|
||||
// ---------------------------------------------------------------------------
|
||||
int CVODESolver::RHS(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
int CVODESolver::RHS(realtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data)
|
||||
{
|
||||
// At this point the up-to-date data for N_Vector y and ydot is on the device.
|
||||
@@ -672,8 +648,7 @@ int CVODESolver::RHS(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int CVODESolver::root(sunrealtype t, N_Vector y, sunrealtype *gout,
|
||||
void *user_data)
|
||||
int CVODESolver::root(realtype t, N_Vector y, realtype *gout, void *user_data)
|
||||
{
|
||||
CVODESolver *self = static_cast<CVODESolver*>(user_data);
|
||||
|
||||
@@ -693,9 +668,8 @@ void CVODESolver::SetRootFinder(int components, RootFunction func)
|
||||
MFEM_VERIFY(flag == CV_SUCCESS, "error in SetRootFinder()");
|
||||
}
|
||||
|
||||
int CVODESolver::LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy,
|
||||
SUNMatrix A, sunbooleantype jok,
|
||||
sunbooleantype *jcur, sunrealtype gamma,
|
||||
int CVODESolver::LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
booleantype jok, booleantype *jcur, realtype gamma,
|
||||
void*, N_Vector, N_Vector, N_Vector)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -709,7 +683,7 @@ int CVODESolver::LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy,
|
||||
}
|
||||
|
||||
int CVODESolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
N_Vector b, sunrealtype tol)
|
||||
N_Vector b, realtype tol)
|
||||
{
|
||||
SundialsNVector mfem_x(x);
|
||||
const SundialsNVector mfem_b(b);
|
||||
@@ -885,7 +859,7 @@ void CVODESolver::UseSundialsLinearSolver()
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
// Create linear solver
|
||||
LSA = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
LSA = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
// Attach linear solver
|
||||
@@ -1176,7 +1150,7 @@ void CVODESSolver::UseSundialsLinearSolverB()
|
||||
if (LSB != NULL) { SUNLinSolFree(LSB); LSB = NULL; }
|
||||
|
||||
// Set default linear solver (Newton is the default Nonlinear Solver)
|
||||
LSB = SUNLinSol_SPGMR(*yB, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
LSB = SUNLinSol_SPGMR(*yB, PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSB, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
/* Attach the matrix and linear solver */
|
||||
@@ -1184,11 +1158,11 @@ void CVODESSolver::UseSundialsLinearSolverB()
|
||||
MFEM_VERIFY(flag == CV_SUCCESS, "error in CVodeSetLinearSolverB()");
|
||||
}
|
||||
|
||||
int CVODESSolver::LinSysSetupB(sunrealtype t, N_Vector y, N_Vector yB,
|
||||
int CVODESSolver::LinSysSetupB(realtype t, N_Vector y, N_Vector yB,
|
||||
N_Vector fyB, SUNMatrix AB,
|
||||
sunbooleantype jokB, sunbooleantype *jcurB,
|
||||
sunrealtype gammaB, void *user_data,
|
||||
N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
|
||||
booleantype jokB, booleantype *jcurB,
|
||||
realtype gammaB, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
const SundialsNVector mfem_y(y);
|
||||
@@ -1204,7 +1178,7 @@ int CVODESSolver::LinSysSetupB(sunrealtype t, N_Vector y, N_Vector yB,
|
||||
}
|
||||
|
||||
int CVODESSolver::LinSysSolveB(SUNLinearSolver LS, SUNMatrix AB, N_Vector yB,
|
||||
N_Vector Rb, sunrealtype tol)
|
||||
N_Vector Rb, realtype tol)
|
||||
{
|
||||
SundialsNVector mfem_yB(yB);
|
||||
const SundialsNVector mfem_Rb(Rb);
|
||||
@@ -1242,7 +1216,7 @@ void CVODESSolver::SetWFTolerances(EWTFunction func)
|
||||
|
||||
// CVODESSolver static functions
|
||||
|
||||
int CVODESSolver::RHSQ(sunrealtype t, const N_Vector y, N_Vector qdot,
|
||||
int CVODESSolver::RHSQ(realtype t, const N_Vector y, N_Vector qdot,
|
||||
void *user_data)
|
||||
{
|
||||
CVODESSolver *self = static_cast<CVODESSolver*>(user_data);
|
||||
@@ -1255,7 +1229,7 @@ int CVODESSolver::RHSQ(sunrealtype t, const N_Vector y, N_Vector qdot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CVODESSolver::RHSQB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector qBdot,
|
||||
int CVODESSolver::RHSQB(realtype t, N_Vector y, N_Vector yB, N_Vector qBdot,
|
||||
void *user_dataB)
|
||||
{
|
||||
CVODESSolver *self = static_cast<CVODESSolver*>(user_dataB);
|
||||
@@ -1269,7 +1243,7 @@ int CVODESSolver::RHSQB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector qBdot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CVODESSolver::RHSB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector yBdot,
|
||||
int CVODESSolver::RHSB(realtype t, N_Vector y, N_Vector yB, N_Vector yBdot,
|
||||
void *user_dataB)
|
||||
{
|
||||
CVODESSolver *self = static_cast<CVODESSolver*>(user_dataB);
|
||||
@@ -1367,7 +1341,7 @@ CVODESSolver::~CVODESSolver()
|
||||
// ARKStep interface
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int ARKStepSolver::RHS1(sunrealtype t, const N_Vector y, N_Vector result,
|
||||
int ARKStepSolver::RHS1(realtype t, const N_Vector y, N_Vector result,
|
||||
void *user_data)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -1399,7 +1373,7 @@ int ARKStepSolver::RHS1(sunrealtype t, const N_Vector y, N_Vector result,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int ARKStepSolver::RHS2(sunrealtype t, const N_Vector y, N_Vector result,
|
||||
int ARKStepSolver::RHS2(realtype t, const N_Vector y, N_Vector result,
|
||||
void *user_data)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -1425,9 +1399,9 @@ int ARKStepSolver::RHS2(sunrealtype t, const N_Vector y, N_Vector result,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int ARKStepSolver::LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy,
|
||||
SUNMatrix A, SUNMatrix, sunbooleantype jok,
|
||||
sunbooleantype *jcur, sunrealtype gamma,
|
||||
int ARKStepSolver::LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
SUNMatrix, booleantype jok, booleantype *jcur,
|
||||
realtype gamma,
|
||||
void*, N_Vector, N_Vector, N_Vector)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -1445,7 +1419,7 @@ int ARKStepSolver::LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy,
|
||||
}
|
||||
|
||||
int ARKStepSolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
N_Vector b, sunrealtype tol)
|
||||
N_Vector b, realtype tol)
|
||||
{
|
||||
SundialsNVector mfem_x(x);
|
||||
const SundialsNVector mfem_b(b);
|
||||
@@ -1459,7 +1433,7 @@ int ARKStepSolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
return (self->f->SUNImplicitSolve(mfem_b, mfem_x, tol));
|
||||
}
|
||||
|
||||
int ARKStepSolver::MassSysSetup(sunrealtype t, SUNMatrix M,
|
||||
int ARKStepSolver::MassSysSetup(realtype t, SUNMatrix M,
|
||||
void*, N_Vector, N_Vector, N_Vector)
|
||||
{
|
||||
ARKStepSolver *self = static_cast<ARKStepSolver*>(GET_CONTENT(M));
|
||||
@@ -1470,7 +1444,7 @@ int ARKStepSolver::MassSysSetup(sunrealtype t, SUNMatrix M,
|
||||
}
|
||||
|
||||
int ARKStepSolver::MassSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
N_Vector b, sunrealtype tol)
|
||||
N_Vector b, realtype tol)
|
||||
{
|
||||
SundialsNVector mfem_x(x);
|
||||
const SundialsNVector mfem_b(b);
|
||||
@@ -1490,7 +1464,7 @@ int ARKStepSolver::MassMult1(SUNMatrix M, N_Vector x, N_Vector v)
|
||||
return (self->f->SUNMassMult(mfem_x, mfem_v));
|
||||
}
|
||||
|
||||
int ARKStepSolver::MassMult2(N_Vector x, N_Vector v, sunrealtype t,
|
||||
int ARKStepSolver::MassMult2(N_Vector x, N_Vector v, realtype t,
|
||||
void* mtimes_data)
|
||||
{
|
||||
const SundialsNVector mfem_x(x);
|
||||
@@ -1561,7 +1535,7 @@ void ARKStepSolver::Init(TimeDependentOperator &f_)
|
||||
// Free existing solver memory and re-create with new vector size
|
||||
if (resize)
|
||||
{
|
||||
MFEM_ARKode(Free)(&sundials_mem);
|
||||
ARKStepFree(&sundials_mem);
|
||||
sundials_mem = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1599,15 +1573,12 @@ void ARKStepSolver::Init(TimeDependentOperator &f_)
|
||||
MFEM_VERIFY(sundials_mem, "error in ARKStepCreate()");
|
||||
|
||||
// Attach the ARKStepSolver as user-defined data
|
||||
flag = MFEM_ARKode(SetUserData)(sundials_mem, this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetUserData)) "()");
|
||||
flag = ARKStepSetUserData(sundials_mem, this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetUserData()");
|
||||
|
||||
// Set default tolerances
|
||||
flag = MFEM_ARKode(SStolerances)(sundials_mem, default_rel_tol,
|
||||
default_abs_tol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SStolerances)) "()");
|
||||
flag = ARKStepSStolerances(sundials_mem, default_rel_tol, default_abs_tol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetSStolerances()");
|
||||
|
||||
// If implicit, attach MFEM linear solver by default
|
||||
if (use_implicit) { UseMFEMLinearSolver(); }
|
||||
@@ -1646,16 +1617,15 @@ void ARKStepSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
// Integrate the system
|
||||
double tout = t + dt;
|
||||
flag = MFEM_ARKode(Evolve)(sundials_mem, tout, *Y, &t, step_mode);
|
||||
MFEM_VERIFY(flag >= 0, "error in " STR(MFEM_ARKode(Evolve)) "()");
|
||||
flag = ARKStepEvolve(sundials_mem, tout, *Y, &t, step_mode);
|
||||
MFEM_VERIFY(flag >= 0, "error in ARKStepEvolve()");
|
||||
|
||||
// Make sure host is up to date
|
||||
Y->HostRead();
|
||||
|
||||
// Return the last incremental step size
|
||||
flag = MFEM_ARKode(GetLastStep)(sundials_mem, &dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(GetLastStep)) "()");
|
||||
flag = ARKStepGetLastStep(sundials_mem, &dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepGetLastStep()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::UseMFEMLinearSolver()
|
||||
@@ -1681,14 +1651,12 @@ void ARKStepSolver::UseMFEMLinearSolver()
|
||||
A->ops->destroy = MatDestroy;
|
||||
|
||||
// Attach the linear solver and matrix
|
||||
flag = MFEM_ARKode(SetLinearSolver)(sundials_mem, LSA, A);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetLinearSolver)) "()");
|
||||
flag = ARKStepSetLinearSolver(sundials_mem, LSA, A);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinearSolver()");
|
||||
|
||||
// Set the linear system evaluation function
|
||||
flag = MFEM_ARKode(SetLinSysFn)(sundials_mem, ARKStepSolver::LinSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetLinSysFn)) "()");
|
||||
flag = ARKStepSetLinSysFn(sundials_mem, ARKStepSolver::LinSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinSysFn()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::UseSundialsLinearSolver()
|
||||
@@ -1698,13 +1666,12 @@ void ARKStepSolver::UseSundialsLinearSolver()
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
// Create linear solver
|
||||
LSA = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
LSA = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
// Attach linear solver
|
||||
flag = MFEM_ARKode(SetLinearSolver)(sundials_mem, LSA, NULL);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetLinearSolver)) "()");
|
||||
flag = ARKStepSetLinearSolver(sundials_mem, LSA, NULL);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinearSolver()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::UseMFEMMassLinearSolver(int tdep)
|
||||
@@ -1731,14 +1698,12 @@ void ARKStepSolver::UseMFEMMassLinearSolver(int tdep)
|
||||
M->ops->destroy = MatDestroy;
|
||||
|
||||
// Attach the linear solver and matrix
|
||||
flag = MFEM_ARKode(SetMassLinearSolver)(sundials_mem, LSM, M, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassLinearSolver)) "()");
|
||||
flag = ARKStepSetMassLinearSolver(sundials_mem, LSM, M, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinearSolver()");
|
||||
|
||||
// Set the linear system function
|
||||
flag = MFEM_ARKode(SetMassFn)(sundials_mem, ARKStepSolver::MassSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassFn)) "()");
|
||||
flag = ARKStepSetMassFn(sundials_mem, ARKStepSolver::MassSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMassFn()");
|
||||
|
||||
// Check that the ODE is not expressed in EXPLICIT form
|
||||
MFEM_VERIFY(!f->isExplicit(), "ODE operator is expressed in EXPLICIT form")
|
||||
@@ -1751,19 +1716,17 @@ void ARKStepSolver::UseSundialsMassLinearSolver(int tdep)
|
||||
if (LSM != NULL) { SUNLinSolFree(LSM); LSM = NULL; }
|
||||
|
||||
// Create linear solver
|
||||
LSM = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
LSM = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSM, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
// Attach linear solver
|
||||
flag = MFEM_ARKode(SetMassLinearSolver)(sundials_mem, LSM, NULL, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassLinearSolver)) "()");
|
||||
flag = ARKStepSetMassLinearSolver(sundials_mem, LSM, NULL, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMassLinearSolver()");
|
||||
|
||||
// Attach matrix multiplication function
|
||||
flag = MFEM_ARKode(SetMassTimes)(sundials_mem, NULL,
|
||||
ARKStepSolver::MassMult2, this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassTimes)) "()");
|
||||
flag = ARKStepSetMassTimes(sundials_mem, NULL, ARKStepSolver::MassMult2,
|
||||
this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMassTimes()");
|
||||
|
||||
// Check that the ODE is not expressed in EXPLICIT form
|
||||
MFEM_VERIFY(!f->isExplicit(), "ODE operator is expressed in EXPLICIT form")
|
||||
@@ -1776,23 +1739,20 @@ void ARKStepSolver::SetStepMode(int itask)
|
||||
|
||||
void ARKStepSolver::SetSStolerances(double reltol, double abstol)
|
||||
{
|
||||
flag = MFEM_ARKode(SStolerances)(sundials_mem, reltol, abstol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SStolerances)) "()");
|
||||
flag = ARKStepSStolerances(sundials_mem, reltol, abstol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSStolerances()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::SetMaxStep(double dt_max)
|
||||
{
|
||||
flag = MFEM_ARKode(SetMaxStep)(sundials_mem, dt_max);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMaxStep)) "()");
|
||||
flag = ARKStepSetMaxStep(sundials_mem, dt_max);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMaxStep()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::SetOrder(int order)
|
||||
{
|
||||
flag = MFEM_ARKode(SetOrder)(sundials_mem, order);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetOrder)) "()");
|
||||
flag = ARKStepSetOrder(sundials_mem, order);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetOrder()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::SetERKTableNum(ARKODE_ERKTableID table_id)
|
||||
@@ -1816,9 +1776,8 @@ void ARKStepSolver::SetIMEXTableNum(ARKODE_ERKTableID etable_id,
|
||||
|
||||
void ARKStepSolver::SetFixedStep(double dt)
|
||||
{
|
||||
flag = MFEM_ARKode(SetFixedStep)(sundials_mem, dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetFixedStep)) "()");
|
||||
flag = ARKStepSetFixedStep(sundials_mem, dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetFixedStep()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::PrintInfo() const
|
||||
@@ -1840,19 +1799,18 @@ void ARKStepSolver::PrintInfo() const
|
||||
&netfails);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepGetTimestepperStats()");
|
||||
|
||||
flag = MFEM_ARKode(GetStepStats)(sundials_mem,
|
||||
&nsteps,
|
||||
&hinused,
|
||||
&hlast,
|
||||
&hcur,
|
||||
&tcur);
|
||||
flag = ARKStepGetStepStats(sundials_mem,
|
||||
&nsteps,
|
||||
&hinused,
|
||||
&hlast,
|
||||
&hcur,
|
||||
&tcur);
|
||||
|
||||
// Get nonlinear solver stats
|
||||
flag = MFEM_ARKode(GetNonlinSolvStats)(sundials_mem,
|
||||
&nniters,
|
||||
&nncfails);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(GetNonlinSolvStats)) "()");
|
||||
flag = ARKStepGetNonlinSolvStats(sundials_mem,
|
||||
&nniters,
|
||||
&nncfails);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepGetNonlinSolvStats()");
|
||||
|
||||
mfem::out <<
|
||||
"ARKStep:\n"
|
||||
@@ -1880,7 +1838,7 @@ ARKStepSolver::~ARKStepSolver()
|
||||
SUNMatDestroy(A);
|
||||
SUNLinSolFree(LSA);
|
||||
SUNNonlinSolFree(NLS);
|
||||
MFEM_ARKode(Free)(&sundials_mem);
|
||||
ARKStepFree(&sundials_mem);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1903,7 +1861,7 @@ int KINSolver::Mult(const N_Vector u, N_Vector fu, void *user_data)
|
||||
|
||||
// Wrapper for computing Jacobian-vector products
|
||||
int KINSolver::GradientMult(N_Vector v, N_Vector Jv, N_Vector u,
|
||||
sunbooleantype *new_u, void *user_data)
|
||||
booleantype *new_u, void *user_data)
|
||||
{
|
||||
const SundialsNVector mfem_v(v);
|
||||
SundialsNVector mfem_Jv(Jv);
|
||||
@@ -1943,7 +1901,7 @@ int KINSolver::LinSysSetup(N_Vector u, N_Vector, SUNMatrix J,
|
||||
|
||||
// Wrapper for solving linear systems J u = b
|
||||
int KINSolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector u,
|
||||
N_Vector b, sunrealtype)
|
||||
N_Vector b, realtype)
|
||||
{
|
||||
SundialsNVector mfem_u(u), mfem_b(b);
|
||||
KINSolver *self = static_cast<KINSolver*>(GET_CONTENT(LS));
|
||||
@@ -2002,11 +1960,7 @@ KINSolver::KINSolver(int strategy, bool oper_grad)
|
||||
f_scale = new SundialsNVector();
|
||||
|
||||
// Default abs_tol and print_level
|
||||
#if MFEM_SUNDIALS_VERSION < 70000
|
||||
abs_tol = pow(UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#else
|
||||
abs_tol = pow(SUN_UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#endif
|
||||
print_level = 0;
|
||||
}
|
||||
|
||||
@@ -2020,11 +1974,7 @@ KINSolver::KINSolver(MPI_Comm comm, int strategy, bool oper_grad)
|
||||
f_scale = new SundialsNVector(comm);
|
||||
|
||||
// Default abs_tol and print_level
|
||||
#if MFEM_SUNDIALS_VERSION < 70000
|
||||
abs_tol = pow(UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#else
|
||||
abs_tol = pow(SUN_UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#endif
|
||||
print_level = 0;
|
||||
}
|
||||
#endif
|
||||
@@ -2136,7 +2086,7 @@ void KINSolver::SetOperator(const Operator &op)
|
||||
if (A != NULL) { SUNMatDestroy(A); A = NULL; }
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
LSA = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
LSA = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
flag = KINSetLinearSolver(sundials_mem, LSA, NULL);
|
||||
@@ -2205,12 +2155,12 @@ void KINSolver::SetJFNKSolver(Solver &solver)
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
// Setup FGMRES
|
||||
LSA = SUNLinSol_SPFGMR(*Y, prec ? SUN_PREC_RIGHT : SUN_PREC_NONE, maxli,
|
||||
LSA = SUNLinSol_SPFGMR(*Y, prec ? PREC_RIGHT : PREC_NONE, maxli,
|
||||
Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPFGMR()");
|
||||
|
||||
flag = SUNLinSol_SPFGMRSetMaxRestarts(LSA, maxlrs);
|
||||
MFEM_VERIFY(flag == SUN_SUCCESS, "error in SUNLinSol_SPFGMR()");
|
||||
MFEM_VERIFY(flag == SUNLS_SUCCESS, "error in SUNLinSol_SPFGMR()");
|
||||
|
||||
flag = KINSetLinearSolver(sundials_mem, LSA, NULL);
|
||||
MFEM_VERIFY(flag == KIN_SUCCESS, "error in KINSetLinearSolver()");
|
||||
@@ -2367,21 +2317,18 @@ void KINSolver::Mult(Vector &x,
|
||||
|
||||
if (rank == 0)
|
||||
{
|
||||
#if MFEM_SUNDIALS_VERSION < 70000
|
||||
flag = KINSetPrintLevel(sundials_mem, print_level);
|
||||
MFEM_VERIFY(flag == KIN_SUCCESS, "KINSetPrintLevel() failed!");
|
||||
#endif
|
||||
// NOTE: there is no KINSetPrintLevel in SUNDIALS v7!
|
||||
|
||||
#ifdef SUNDIALS_BUILD_WITH_MONITORING
|
||||
if (jfnk && print_level)
|
||||
{
|
||||
flag = SUNLinSolSetInfoFile_SPFGMR(LSA, stdout);
|
||||
MFEM_VERIFY(flag == SUN_SUCCESS,
|
||||
MFEM_VERIFY(flag == SUNLS_SUCCESS,
|
||||
"error in SUNLinSolSetInfoFile_SPFGMR()");
|
||||
|
||||
flag = SUNLinSolSetPrintLevel_SPFGMR(LSA, 1);
|
||||
MFEM_VERIFY(flag == SUN_SUCCESS,
|
||||
MFEM_VERIFY(flag == SUNLS_SUCCESS,
|
||||
"error in SUNLinSolSetPrintLevel_SPFGMR()");
|
||||
}
|
||||
#endif
|
||||
|
||||
+31
-65
@@ -54,10 +54,6 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define MFEM_SUNDIALS_VERSION \
|
||||
(SUNDIALS_VERSION_MAJOR*10000 + SUNDIALS_VERSION_MINOR*100 + \
|
||||
SUNDIALS_VERSION_PATCH)
|
||||
|
||||
#if (SUNDIALS_VERSION_MAJOR < 6)
|
||||
|
||||
/// (DEPRECATED) Map SUNDIALS version >= 6 datatypes and constants to
|
||||
@@ -72,30 +68,13 @@ constexpr ARKODE_ERKTableID ARKODE_FEHLBERG_13_7_8 = FEHLBERG_13_7_8;
|
||||
/// arbitrary type for more compact backwards compatibility
|
||||
using SUNContext = void*;
|
||||
|
||||
/// 'sunrealtype' was first introduced in v6.0.0
|
||||
typedef realtype sunrealtype;
|
||||
/// 'sunbooleantype' was first introduced in v6.0.0
|
||||
typedef booleantype sunbooleantype;
|
||||
|
||||
/// New constant names introduced in v6.0.0
|
||||
enum { SUN_PREC_NONE, SUN_PREC_LEFT, SUN_PREC_RIGHT, SUN_PREC_BOTH };
|
||||
|
||||
// KIN_ORTH_MGS was introduced in SUNDIALS v6; here, we define it just so that
|
||||
// it can be used as the default option in the second parameter of
|
||||
// KINSolver::EnableAndersonAcc -- the actual value of the parameter will be
|
||||
// ignored when using SUNDIALS < v6.
|
||||
#define KIN_ORTH_MGS 0
|
||||
|
||||
#endif // #if SUNDIALS_VERSION_MAJOR < 6
|
||||
|
||||
#if (SUNDIALS_VERSION_MAJOR < 7)
|
||||
|
||||
/** @brief The enum constant SUN_SUCCESS was added in v7 as a replacement of
|
||||
various *_SUCCESS macros that were removed in v7. */
|
||||
enum { SUN_SUCCESS = 0 };
|
||||
|
||||
#endif // #if SUNDIALS_VERSION_MAJOR < 7
|
||||
|
||||
#endif // SUNDIALS_VERSION_MAJOR < 6
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -265,14 +244,7 @@ public:
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Returns the MPI communicator for the internal N_Vector x.
|
||||
inline MPI_Comm GetComm() const
|
||||
{
|
||||
#if SUNDIALS_VERSION_MAJOR < 7
|
||||
return *static_cast<MPI_Comm*>(N_VGetCommunicator(x));
|
||||
#else
|
||||
return N_VGetCommunicator(x);
|
||||
#endif
|
||||
}
|
||||
inline MPI_Comm GetComm() const { return *static_cast<MPI_Comm*>(N_VGetCommunicator(x)); }
|
||||
|
||||
/// Returns the MPI global length for the internal N_Vector x.
|
||||
inline long GlobalSize() const { return N_VGetLength(x); }
|
||||
@@ -424,26 +396,24 @@ protected:
|
||||
int root_components; /// Number of components in gout
|
||||
|
||||
/// Wrapper to compute the ODE rhs function.
|
||||
static int RHS(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data);
|
||||
static int RHS(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
|
||||
|
||||
/// Setup the linear system $ A x = b $.
|
||||
static int LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
sunbooleantype jok, sunbooleantype *jcur,
|
||||
sunrealtype gamma, void *user_data, N_Vector tmp1,
|
||||
static int LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
booleantype jok, booleantype *jcur,
|
||||
realtype gamma, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system $ A x = b $.
|
||||
static int LinSysSolve(SUNLinearSolver LS, SUNMatrix A, N_Vector x,
|
||||
N_Vector b, sunrealtype tol);
|
||||
N_Vector b, realtype tol);
|
||||
|
||||
/// Prototype to define root finding for CVODE
|
||||
static int root(sunrealtype t, N_Vector y, sunrealtype *gout,
|
||||
void *user_data);
|
||||
static int root(realtype t, N_Vector y, realtype *gout, void *user_data);
|
||||
|
||||
/// Typedef for root finding functions
|
||||
typedef std::function<int(sunrealtype t, Vector y, Vector gout,
|
||||
CVODESolver *)> RootFunction;
|
||||
typedef std::function<int(realtype t, Vector y, Vector gout, CVODESolver *)>
|
||||
RootFunction;
|
||||
|
||||
/// A class member to facilitate pointing to a user-specified root function
|
||||
RootFunction root_func;
|
||||
@@ -451,8 +421,7 @@ protected:
|
||||
/// Typedef declaration for error weight functions
|
||||
typedef std::function<int(Vector y, Vector w, CVODESolver*)> EWTFunction;
|
||||
|
||||
/** @brief A class member to facilitate pointing to a user-specified error
|
||||
weight function */
|
||||
/// A class member to facilitate pointing to a user-specified error weight function
|
||||
EWTFunction ewt_func;
|
||||
|
||||
public:
|
||||
@@ -486,7 +455,7 @@ public:
|
||||
@note If this method is called a second time with a different problem
|
||||
size, then any non-default user-set options will be lost and will need
|
||||
to be set again. */
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
void Init(TimeDependentOperator &f_);
|
||||
|
||||
/// Integrate the ODE with CVODE using the specified step mode.
|
||||
/** @param[in,out] x On output, the solution vector at the requested output
|
||||
@@ -562,15 +531,14 @@ protected:
|
||||
int indexB; ///< backward problem index
|
||||
|
||||
/// Wrapper to compute the ODE RHS Quadrature function.
|
||||
static int RHSQ(sunrealtype t, const N_Vector y, N_Vector qdot,
|
||||
void *user_data);
|
||||
static int RHSQ(realtype t, const N_Vector y, N_Vector qdot, void *user_data);
|
||||
|
||||
/// Wrapper to compute the ODE RHS backward function.
|
||||
static int RHSB(sunrealtype t, N_Vector y,
|
||||
static int RHSB(realtype t, N_Vector y,
|
||||
N_Vector yB, N_Vector yBdot, void *user_dataB);
|
||||
|
||||
/// Wrapper to compute the ODE RHS Backwards Quadrature function.
|
||||
static int RHSQB(sunrealtype t, N_Vector y, N_Vector yB,
|
||||
static int RHSQB(realtype t, N_Vector y, N_Vector yB,
|
||||
N_Vector qBdot, void *user_dataB);
|
||||
|
||||
/// Error control function
|
||||
@@ -686,15 +654,15 @@ public:
|
||||
void SetSVtolerancesB(double reltol, Vector abstol);
|
||||
|
||||
/// Setup the linear system A x = b
|
||||
static int LinSysSetupB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector fyB,
|
||||
static int LinSysSetupB(realtype t, N_Vector y, N_Vector yB, N_Vector fyB,
|
||||
SUNMatrix A,
|
||||
sunbooleantype jok, sunbooleantype *jcur,
|
||||
sunrealtype gamma, void *user_data, N_Vector tmp1,
|
||||
booleantype jok, booleantype *jcur,
|
||||
realtype gamma, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system A x = b
|
||||
static int LinSysSolveB(SUNLinearSolver LS, SUNMatrix A, N_Vector x,
|
||||
N_Vector b, sunrealtype tol);
|
||||
N_Vector b, realtype tol);
|
||||
|
||||
|
||||
/// Destroy the associated CVODES memory and SUNDIALS objects.
|
||||
@@ -727,35 +695,33 @@ protected:
|
||||
RHS1 is explicit RHS and RHS2 the implicit RHS for IMEX integration. When
|
||||
purely implicit or explicit only RHS1 is used. */
|
||||
///@{
|
||||
static int RHS1(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data);
|
||||
static int RHS2(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data);
|
||||
static int RHS1(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
|
||||
static int RHS2(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
|
||||
///@}
|
||||
|
||||
/// Setup the linear system $ A x = b $.
|
||||
static int LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
SUNMatrix M, sunbooleantype jok, sunbooleantype *jcur,
|
||||
sunrealtype gamma, void *user_data, N_Vector tmp1,
|
||||
static int LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
SUNMatrix M, booleantype jok, booleantype *jcur,
|
||||
realtype gamma, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system $ A x = b $.
|
||||
static int LinSysSolve(SUNLinearSolver LS, SUNMatrix A, N_Vector x,
|
||||
N_Vector b, sunrealtype tol);
|
||||
N_Vector b, realtype tol);
|
||||
|
||||
/// Setup the linear system $ M x = b $.
|
||||
static int MassSysSetup(sunrealtype t, SUNMatrix M, void *user_data,
|
||||
static int MassSysSetup(realtype t, SUNMatrix M, void *user_data,
|
||||
N_Vector tmp1, N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system $ M x = b $.
|
||||
static int MassSysSolve(SUNLinearSolver LS, SUNMatrix M, N_Vector x,
|
||||
N_Vector b, sunrealtype tol);
|
||||
N_Vector b, realtype tol);
|
||||
|
||||
/// Compute the matrix-vector product $ v = M x $.
|
||||
static int MassMult1(SUNMatrix M, N_Vector x, N_Vector v);
|
||||
|
||||
/// Compute the matrix-vector product $v = M_t x $ at time t.
|
||||
static int MassMult2(N_Vector x, N_Vector v, sunrealtype t,
|
||||
static int MassMult2(N_Vector x, N_Vector v, realtype t,
|
||||
void* mtimes_data);
|
||||
|
||||
public:
|
||||
@@ -791,7 +757,7 @@ public:
|
||||
@note If this method is called a second time with a different problem
|
||||
size, then any non-default user-set options will be lost and will need
|
||||
to be set again. */
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
void Init(TimeDependentOperator &f_);
|
||||
|
||||
/// Integrate the ODE with ARKode using the specified step mode.
|
||||
/**
|
||||
@@ -905,7 +871,7 @@ protected:
|
||||
|
||||
/// Wrapper to compute the Jacobian-vector product $ J(u) v = Jv $.
|
||||
static int GradientMult(N_Vector v, N_Vector Jv, N_Vector u,
|
||||
sunbooleantype *new_u, void *user_data);
|
||||
booleantype *new_u, void *user_data);
|
||||
|
||||
/// Setup the linear system $ J u = b $.
|
||||
static int LinSysSetup(N_Vector u, N_Vector fu, SUNMatrix J,
|
||||
@@ -913,7 +879,7 @@ protected:
|
||||
|
||||
/// Solve the linear system $ J u = b $.
|
||||
static int LinSysSolve(SUNLinearSolver LS, SUNMatrix J, N_Vector u,
|
||||
N_Vector b, sunrealtype tol);
|
||||
N_Vector b, realtype tol);
|
||||
|
||||
/// Setup the preconditioner.
|
||||
static int PrecSetup(N_Vector uu,
|
||||
|
||||
@@ -79,7 +79,6 @@ inline real_t rand_real()
|
||||
class Vector
|
||||
{
|
||||
protected:
|
||||
template<typename mfem_type, int N, typename L> friend class MDSpan;
|
||||
|
||||
Memory<real_t> data;
|
||||
int size;
|
||||
|
||||
+4
-8
@@ -32,7 +32,6 @@ set(SRCS
|
||||
vtk.cpp
|
||||
wedge.cpp
|
||||
submesh/submesh.cpp
|
||||
submesh/ncsubmesh.cpp
|
||||
submesh/submesh_utils.cpp
|
||||
submesh/transfermap.cpp
|
||||
)
|
||||
@@ -59,7 +58,6 @@ set(HDRS
|
||||
vertex.hpp
|
||||
vtk.hpp
|
||||
wedge.hpp
|
||||
submesh/ncsubmesh.hpp
|
||||
submesh/submesh.hpp
|
||||
submesh/submesh_utils.hpp
|
||||
submesh/transfer_category.hpp
|
||||
@@ -70,17 +68,15 @@ if (MFEM_USE_MPI)
|
||||
list(APPEND SRCS
|
||||
pmesh.cpp
|
||||
pncmesh.cpp
|
||||
submesh/pncsubmesh.cpp
|
||||
submesh/psubmesh.cpp
|
||||
submesh/ptransfermap.cpp)
|
||||
submesh/ptransfermap.cpp
|
||||
submesh/psubmesh.cpp)
|
||||
# If this list (HDRS -> HEADERS) is used for install, we probably want the
|
||||
# headers added all the time.
|
||||
list(APPEND HDRS
|
||||
pmesh.hpp
|
||||
pncmesh.hpp
|
||||
submesh/pncsubmesh.hpp
|
||||
submesh/psubmesh.hpp
|
||||
submesh/ptransfermap.hpp)
|
||||
submesh/ptransfermap.hpp
|
||||
submesh/psubmesh.hpp)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_PUMI)
|
||||
|
||||
+1
-18
@@ -2033,18 +2033,6 @@ int Mesh::AddBdrElement(Element *elem)
|
||||
return NumOfBdrElements++;
|
||||
}
|
||||
|
||||
void Mesh::AddBdrElements(Array<Element *> &bdr_elems,
|
||||
const Array<int> &new_be_to_face)
|
||||
{
|
||||
boundary.Reserve(boundary.Size() + bdr_elems.Size());
|
||||
MFEM_ASSERT(bdr_elems.Size() == new_be_to_face.Size(), "wrong size");
|
||||
for (int i = 0; i < bdr_elems.Size(); i++)
|
||||
{
|
||||
AddBdrElement(bdr_elems[i]);
|
||||
}
|
||||
be_to_face.Append(new_be_to_face);
|
||||
}
|
||||
|
||||
int Mesh::AddBdrSegment(int v1, int v2, int attr)
|
||||
{
|
||||
CheckEnlarge(boundary, NumOfBdrElements);
|
||||
@@ -7358,12 +7346,6 @@ void Mesh::GetBdrElementAdjacentElement2(
|
||||
info = fi.Elem1Inf + ori;
|
||||
}
|
||||
|
||||
void Mesh::SetAttribute(int i, int attr)
|
||||
{
|
||||
elements[i]->SetAttribute(attr);
|
||||
if (ncmesh) ncmesh->SetAttribute(i, attr);
|
||||
}
|
||||
|
||||
Element::Type Mesh::GetElementType(int i) const
|
||||
{
|
||||
return elements[i]->GetType();
|
||||
@@ -7690,6 +7672,7 @@ void Mesh::AddQuadFaceElement(int lf, int gf, int el,
|
||||
void Mesh::GenerateFaces()
|
||||
{
|
||||
int nfaces = GetNumFaces();
|
||||
|
||||
for (auto &f : faces)
|
||||
{
|
||||
FreeElement(f);
|
||||
|
||||
+1
-21
@@ -993,17 +993,6 @@ public:
|
||||
/// @note Ownership of @a elem will pass to the Mesh object
|
||||
int AddBdrElement(Element *elem);
|
||||
|
||||
/**
|
||||
* @brief Add an array of boundary elements to the mesh, along with map from
|
||||
* the elements to their faces
|
||||
* @param[in] bdr_elems The set of boundary element pointers, ownership of
|
||||
* the pointers will be transferred to the Mesh object
|
||||
* @param[in] be_to_face The map from the boundary element index to the face
|
||||
* index
|
||||
*/
|
||||
void AddBdrElements(Array<Element *> &bdr_elems,
|
||||
const Array<int> &be_to_face);
|
||||
|
||||
int AddBdrSegment(int v1, int v2, int attr = 1);
|
||||
int AddBdrSegment(const int *vi, int attr = 1);
|
||||
|
||||
@@ -1113,15 +1102,6 @@ public:
|
||||
have two adjacent faces in 3D, or edges in 2D. */
|
||||
void RemoveInternalBoundaries();
|
||||
|
||||
/**
|
||||
* @brief Clear the boundary element to edge map.
|
||||
*/
|
||||
void DeleteBoundaryElementToEdge()
|
||||
{
|
||||
delete bel_to_edge;
|
||||
bel_to_edge = nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Element ordering methods
|
||||
@@ -1386,7 +1366,7 @@ public:
|
||||
int GetAttribute(int i) const { return elements[i]->GetAttribute(); }
|
||||
|
||||
/// Set the attribute of element i.
|
||||
void SetAttribute(int i, int attr);
|
||||
void SetAttribute(int i, int attr) { elements[i]->SetAttribute(attr); }
|
||||
|
||||
/// Return the attribute of boundary element i.
|
||||
int GetBdrAttribute(int i) const { return boundary[i]->GetAttribute(); }
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "ncmesh.hpp"
|
||||
#include "mesh.hpp"
|
||||
#include "mesh_operators.hpp"
|
||||
#include "submesh/ncsubmesh.hpp"
|
||||
#include "submesh/submesh.hpp"
|
||||
#include "submesh/submesh_utils.hpp"
|
||||
#include "submesh/transfermap.hpp"
|
||||
@@ -37,7 +36,6 @@
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pncmesh.hpp"
|
||||
#include "pmesh.hpp"
|
||||
#include "submesh/pncsubmesh.hpp"
|
||||
#include "submesh/psubmesh.hpp"
|
||||
#include "submesh/ptransfermap.hpp"
|
||||
#endif
|
||||
|
||||
+65
-249
@@ -58,25 +58,23 @@ void NCMesh::GeomInfo::InitGeom(Geometry::Type geom)
|
||||
{
|
||||
if (initialized) { return; }
|
||||
|
||||
auto elem = [&]()
|
||||
mfem::Element *elem = NULL;
|
||||
switch (geom)
|
||||
{
|
||||
switch (geom)
|
||||
{
|
||||
case Geometry::CUBE: return std::unique_ptr<mfem::Element>(new Hexahedron);
|
||||
case Geometry::PRISM: return std::unique_ptr<mfem::Element>(new Wedge);
|
||||
case Geometry::TETRAHEDRON: return std::unique_ptr<mfem::Element>
|
||||
(new Tetrahedron);
|
||||
case Geometry::PYRAMID: return std::unique_ptr<mfem::Element>(new Pyramid);
|
||||
case Geometry::SQUARE: return std::unique_ptr<mfem::Element>(new Quadrilateral);
|
||||
case Geometry::TRIANGLE: return std::unique_ptr<mfem::Element>(new Triangle);
|
||||
case Geometry::SEGMENT: return std::unique_ptr<mfem::Element>(new Segment);
|
||||
default: MFEM_ABORT("unsupported geometry " << geom);
|
||||
}
|
||||
}();
|
||||
case Geometry::CUBE: elem = new Hexahedron; break;
|
||||
case Geometry::PRISM: elem = new Wedge; break;
|
||||
case Geometry::TETRAHEDRON: elem = new Tetrahedron; break;
|
||||
case Geometry::PYRAMID: elem = new Pyramid; break;
|
||||
case Geometry::SQUARE: elem = new Quadrilateral; break;
|
||||
case Geometry::TRIANGLE: elem = new Triangle; break;
|
||||
case Geometry::SEGMENT: elem = new Segment; break;
|
||||
default: MFEM_ABORT("unsupported geometry " << geom);
|
||||
}
|
||||
|
||||
nv = elem->GetNVertices();
|
||||
ne = elem->GetNEdges();
|
||||
nf = elem->GetNFaces();
|
||||
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
@@ -121,9 +119,19 @@ void NCMesh::GeomInfo::InitGeom(Geometry::Type geom)
|
||||
}
|
||||
}
|
||||
|
||||
delete elem;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
static void CheckSupportedGeom(Geometry::Type geom)
|
||||
{
|
||||
MFEM_VERIFY(geom == Geometry::SEGMENT ||
|
||||
geom == Geometry::TRIANGLE || geom == Geometry::SQUARE ||
|
||||
geom == Geometry::CUBE || geom == Geometry::PRISM ||
|
||||
geom == Geometry::PYRAMID || geom == Geometry::TETRAHEDRON,
|
||||
"Element type " << geom << " is not supported by NCMesh.");
|
||||
}
|
||||
|
||||
NCMesh::NCMesh(const Mesh *mesh)
|
||||
: shadow(1024, 2048)
|
||||
{
|
||||
@@ -149,7 +157,7 @@ NCMesh::NCMesh(const Mesh *mesh)
|
||||
}
|
||||
|
||||
// create NCMesh::Element for this mfem::Element
|
||||
int root_id = AddElement(geom, elem->GetAttribute());
|
||||
int root_id = AddElement(Element(geom, elem->GetAttribute()));
|
||||
MFEM_ASSERT(root_id == i, "");
|
||||
Element &root_elem = elements[root_id];
|
||||
|
||||
@@ -240,18 +248,11 @@ NCMesh::NCMesh(const NCMesh &other)
|
||||
, nodes(other.nodes)
|
||||
, faces(other.faces)
|
||||
, elements(other.elements)
|
||||
, free_element_ids(other.free_element_ids)
|
||||
, root_state(other.root_state)
|
||||
, coordinates(other.coordinates)
|
||||
, NEdges(other.NEdges)
|
||||
, NFaces(other.NFaces)
|
||||
, NGhostEdges(other.NGhostEdges)
|
||||
, NGhostFaces(other.NGhostFaces)
|
||||
, boundary_faces(other.boundary_faces)
|
||||
, face_geom(other.face_geom)
|
||||
, element_vertex(other.element_vertex)
|
||||
, shadow(1024, 2048)
|
||||
{
|
||||
other.free_element_ids.Copy(free_element_ids);
|
||||
other.root_state.Copy(root_state);
|
||||
other.coordinates.Copy(coordinates);
|
||||
Update();
|
||||
}
|
||||
|
||||
@@ -350,8 +351,8 @@ int NCMesh::GetMidFaceNode(int en1, int en2, int en3, int en4)
|
||||
|
||||
void NCMesh::ReferenceElement(int elem)
|
||||
{
|
||||
const Element &el = elements[elem];
|
||||
const int* node = el.node;
|
||||
Element &el = elements[elem];
|
||||
int* node = el.node;
|
||||
GeomInfo& gi = GI[el.Geom()];
|
||||
|
||||
// reference all vertices
|
||||
@@ -506,7 +507,7 @@ int NCMesh::NewHexahedron(int n0, int n1, int n2, int n3,
|
||||
int fattr3, int fattr4, int fattr5)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::CUBE, attr);
|
||||
int new_id = AddElement(Element(Geometry::CUBE, attr));
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -536,7 +537,7 @@ int NCMesh::NewWedge(int n0, int n1, int n2,
|
||||
int fattr2, int fattr3, int fattr4)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::PRISM, attr);
|
||||
int new_id = AddElement(Element(Geometry::PRISM, attr));
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2;
|
||||
@@ -565,7 +566,7 @@ int NCMesh::NewTetrahedron(int n0, int n1, int n2, int n3, int attr,
|
||||
int fattr0, int fattr1, int fattr2, int fattr3)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::TETRAHEDRON, attr);
|
||||
int new_id = AddElement(Element(Geometry::TETRAHEDRON, attr));
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -591,7 +592,7 @@ int NCMesh::NewPyramid(int n0, int n1, int n2, int n3, int n4, int attr,
|
||||
int fattr4)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::PYRAMID, attr);
|
||||
int new_id = AddElement(Element(Geometry::PYRAMID, attr));
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -621,7 +622,7 @@ int NCMesh::NewQuadrilateral(int n0, int n1, int n2, int n3,
|
||||
int eattr0, int eattr1, int eattr2, int eattr3)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::SQUARE, attr);
|
||||
int new_id = AddElement(Element(Geometry::SQUARE, attr));
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -646,7 +647,7 @@ int NCMesh::NewTriangle(int n0, int n1, int n2,
|
||||
int attr, int eattr0, int eattr1, int eattr2)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::TRIANGLE, attr);
|
||||
int new_id = AddElement(Element(Geometry::TRIANGLE, attr));
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2;
|
||||
@@ -671,7 +672,7 @@ int NCMesh::NewTriangle(int n0, int n1, int n2,
|
||||
int NCMesh::NewSegment(int n0, int n1, int attr, int vattr1, int vattr2)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Geometry::SEGMENT, attr);
|
||||
int new_id = AddElement(Element(Geometry::SEGMENT, attr));
|
||||
Element &el = elements[new_id];
|
||||
el.node[0] = n0, el.node[1] = n1;
|
||||
|
||||
@@ -2166,6 +2167,7 @@ void NCMesh::UpdateLeafElements()
|
||||
// final (Mesh) indices of leaves
|
||||
leaf_elements.Append(ghosts);
|
||||
leaf_sfc_index.SetSize(leaf_elements.Size());
|
||||
|
||||
for (int i = 0; i < leaf_elements.Size(); i++)
|
||||
{
|
||||
Element &el = elements[leaf_elements[i]];
|
||||
@@ -2232,6 +2234,7 @@ void NCMesh::UpdateVertices()
|
||||
}
|
||||
|
||||
// STEP 2: assign indices of top-level local vertices, in original order
|
||||
|
||||
NVertices = 0;
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
@@ -2243,6 +2246,7 @@ void NCMesh::UpdateVertices()
|
||||
|
||||
// STEP 3: go over all elements (local and ghost) in SFC order and assign
|
||||
// remaining local vertices in that order.
|
||||
|
||||
Array<int> sfc_order(leaf_elements.Size());
|
||||
for (int i = 0; i < sfc_order.Size(); i++)
|
||||
{
|
||||
@@ -2260,6 +2264,7 @@ void NCMesh::UpdateVertices()
|
||||
}
|
||||
|
||||
// STEP 4: create the mapping from Mesh vertex index to NCMesh node index
|
||||
|
||||
vertex_nodeId.SetSize(NVertices);
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
@@ -2272,6 +2277,7 @@ void NCMesh::UpdateVertices()
|
||||
|
||||
// STEP 5: assign remaining ghost vertices, ignore vertices beyond the ghost
|
||||
// layer
|
||||
|
||||
NGhostVertices = 0;
|
||||
for (int i = 0; i < sfc_order.Size(); i++)
|
||||
{
|
||||
@@ -2355,8 +2361,6 @@ void NCMesh::InitRootState(int root_count)
|
||||
root_state.SetSize(root_count);
|
||||
root_state = 0;
|
||||
|
||||
if (elements.Size() == 0) { return; }
|
||||
|
||||
char* node_order;
|
||||
int nch;
|
||||
|
||||
@@ -2606,10 +2610,11 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
{
|
||||
const int *ev = edge_vertex->GetRow(i);
|
||||
Node* node = nodes.Find(vertex_nodeId[ev[0]], vertex_nodeId[ev[1]]);
|
||||
|
||||
MFEM_ASSERT(node && node->HasEdge(),
|
||||
"edge (" << ev[0] << "," << ev[1] << ") not found, "
|
||||
"node = " << node << " node->HasEdge() "
|
||||
<< (node != nullptr ? node->HasEdge() : false));
|
||||
"node = " << node);
|
||||
|
||||
node->edge_index = i;
|
||||
}
|
||||
|
||||
@@ -2704,6 +2709,7 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
if (face.index < 0)
|
||||
{
|
||||
face.index = NFaces + (nghosts++);
|
||||
|
||||
// store the face geometry
|
||||
static const Geometry::Type types[5] =
|
||||
{
|
||||
@@ -2787,186 +2793,10 @@ bool NCMesh::TriFaceSplit(int v1, int v2, int v3, int mid[3]) const
|
||||
if (mid) { mid[0] = e1, mid[1] = e2, mid[2] = e3; }
|
||||
|
||||
// This is necessary but not sufficient to determine if a face has been
|
||||
// split. All edges might have been split due to edge attached faces being
|
||||
// refined. Need to check for existence of face made up of midpoints.
|
||||
// split.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool contains_node(const std::array<int, 4> &nodes, int n)
|
||||
{
|
||||
return std::find(nodes.begin(), nodes.end(), n) != nodes.end();
|
||||
};
|
||||
|
||||
int NCMesh::ParentFaceNodes(std::array<int, 4> &face_nodes) const
|
||||
{
|
||||
const bool is_tri = face_nodes[3] == -1;
|
||||
const bool is_segment = (face_nodes[0] == face_nodes[1] &&
|
||||
face_nodes[2] == face_nodes[3]);
|
||||
const bool is_quad = *std::min_element(face_nodes.begin(),
|
||||
face_nodes.end()) >= 0;
|
||||
|
||||
MFEM_ASSERT((is_tri && !is_segment && !is_quad)
|
||||
|| (!is_tri && is_segment && !is_quad) || (!is_tri && !is_segment &&
|
||||
is_quad), "Inconsistent node geometry");
|
||||
|
||||
bool all_nodes_root = true;
|
||||
for (auto x : face_nodes)
|
||||
{
|
||||
all_nodes_root = all_nodes_root && (x < 0 || (nodes[x].p1 == nodes[x].p2));
|
||||
}
|
||||
// This face is a root face -> nothing to do.
|
||||
if (all_nodes_root) { return -1; }
|
||||
|
||||
int child = -1; // The index into parent.child that this face corresponds to.
|
||||
auto parent_nodes = face_nodes;
|
||||
if (is_quad)
|
||||
{
|
||||
// Logic for coarsening anisotropic faces is more complex, needs
|
||||
// identification and handling of multiple "crux" points. Will require
|
||||
// inspection of edge nodes.
|
||||
MFEM_VERIFY(Iso,
|
||||
"ParentFaceNodes does not support anisotropic refinement yet!");
|
||||
|
||||
// Finds the first node whose parents aren't in the face_nodes. This is
|
||||
// also the index of the child location in the parent face. Treated
|
||||
// separately as ultimately multiple crux will need to be handled for
|
||||
// anisotropic faces.
|
||||
const auto crux = [&]()
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(face_nodes.size()); i++)
|
||||
{
|
||||
if ((!contains_node(face_nodes, nodes[face_nodes[i]].p1)
|
||||
&& !contains_node(face_nodes, nodes[face_nodes[i]].p2))
|
||||
|| (nodes[face_nodes[i]].p1 == nodes[face_nodes[i]].p2) /* top level node */)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}();
|
||||
MFEM_ASSERT(crux != -1, "A root face should have been returned early");
|
||||
|
||||
// Loop over nodes, starting from diagonal to child, wrapping and skipping
|
||||
// child. This will visit the node opposite child twice, thereby
|
||||
// coarsening to the diagonally opposite. NOTE: This assumes that the
|
||||
// nodes for a square are numbered (0 -> 1 -> 2 -> 3 -> 0).
|
||||
for (int i = 0; i < static_cast<int>(face_nodes.size()) + 1; i++)
|
||||
{
|
||||
int ind = (crux + i + 2) %
|
||||
4; // Start and end with coarsening of the diagonally opposite
|
||||
if (ind == crux) { continue; }
|
||||
auto &x = parent_nodes[ind];
|
||||
|
||||
// Check against parent_nodes rather than face_nodes so on second lap
|
||||
// the node opposite crux will coarsen again to the diagonally across
|
||||
// in the parent face. A top level node has p1 == p2, thus these
|
||||
// modifications do nothing.
|
||||
if (contains_node(parent_nodes, nodes[x].p1))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(parent_nodes, nodes[x].p2), "!");
|
||||
x = nodes[x].p2;
|
||||
}
|
||||
else if (contains_node(parent_nodes, nodes[x].p2))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(parent_nodes, nodes[x].p1), "!");
|
||||
x = nodes[x].p1;
|
||||
}
|
||||
else { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
else if (is_tri)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto x = face_nodes[i];
|
||||
if (x == -1) { continue; }
|
||||
if (contains_node(face_nodes, nodes[x].p1))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(face_nodes, nodes[x].p2), "!");
|
||||
parent_nodes[i] = nodes[x].p2;
|
||||
}
|
||||
else if (contains_node(face_nodes, nodes[x].p2))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(face_nodes, nodes[x].p1), "!");
|
||||
parent_nodes[i] = nodes[x].p1;
|
||||
}
|
||||
else { /* do nothing */ }
|
||||
}
|
||||
|
||||
if (std::equal(face_nodes.begin(), face_nodes.end(), parent_nodes.begin()))
|
||||
{
|
||||
// Having excluded root faces, this must be an interior face. We need
|
||||
// to handle the special case of the interior face of the parent face.
|
||||
std::array<std::array<int, 2>, 6> parent_pairs;
|
||||
for (std::size_t i = 0; i < face_nodes.size() - 1; i++)
|
||||
{
|
||||
parent_pairs[i][0] = nodes[face_nodes[i]].p1;
|
||||
parent_pairs[i][1] = nodes[face_nodes[i]].p2;
|
||||
}
|
||||
// Each node gets mapped to the common node from its parents and the
|
||||
// predecessor node's parents.
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// Parenting convention here assumes parent face has the SAME
|
||||
// orientation as the original. This is true on exterior boundaries,
|
||||
// but for an interior boundary the master face will have an
|
||||
// opposing orientation. TODO: Possibly fix for interior boundaries.
|
||||
const auto &prev = parent_pairs[(i - 1 + 3) % 3]; // (0 -> 2, 1 -> 0, 2 -> 1)
|
||||
const auto &next = parent_pairs[(i + 1 + 3) % 3]; // (0 -> 1, 1 -> 2, 2 -> 0)
|
||||
for (auto x : next)
|
||||
{
|
||||
if (std::find(prev.begin(), prev.end(), x) != prev.end()) { parent_nodes[i] = x; }
|
||||
}
|
||||
}
|
||||
child = 3; // The interior face is the final child.
|
||||
}
|
||||
}
|
||||
else if (is_segment)
|
||||
{
|
||||
// Given this isn't a root face, one node must be the parent of the other.
|
||||
if (face_nodes[0] == nodes[face_nodes[1]].p1)
|
||||
{
|
||||
face_nodes[1] = nodes[face_nodes[1]].p2;
|
||||
}
|
||||
else if (face_nodes[0] == nodes[face_nodes[1]].p2)
|
||||
{
|
||||
face_nodes[1] = nodes[face_nodes[1]].p1;
|
||||
}
|
||||
else if (face_nodes[1] == nodes[face_nodes[0]].p1)
|
||||
{
|
||||
face_nodes[0] = nodes[face_nodes[0]].p2;
|
||||
}
|
||||
else if (face_nodes[1] == nodes[face_nodes[0]].p2)
|
||||
{
|
||||
face_nodes[0] = nodes[face_nodes[0]].p1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Internal logic error!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unrecognized face geometry!");
|
||||
}
|
||||
for (int i = 0; i < 4 && face_nodes[i] >= 0; i++)
|
||||
{
|
||||
if (face_nodes[i] == parent_nodes[i])
|
||||
{
|
||||
MFEM_ASSERT(child == -1,
|
||||
"This face cannot be more than one child of the parent face!");
|
||||
child = i;
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(child != -1, "Root elements must have exited early!");
|
||||
std::swap(face_nodes, parent_nodes);
|
||||
return child;
|
||||
}
|
||||
|
||||
int NCMesh::find_node(const Element &el, int node)
|
||||
{
|
||||
for (int i = 0; i < MaxElemNodes; i++)
|
||||
@@ -3726,8 +3556,7 @@ NCMesh::NCList::BuildIndex() const
|
||||
int max_master_index = max_master != nullptr ? max_master->index : -1;
|
||||
int max_slave_index = max_slave != nullptr ? max_slave->index : -1;
|
||||
|
||||
inv_index.reserve(max(max_conforming_index, max_master_index, max_slave_index,
|
||||
0));
|
||||
inv_index.reserve(std::max({max_conforming_index, max_master_index, max_slave_index}));
|
||||
for (int i = 0; i < conforming.Size(); i++)
|
||||
{
|
||||
inv_index.emplace(conforming[i].index, std::make_pair(MeshIdType::CONFORMING,
|
||||
@@ -3742,6 +3571,8 @@ NCMesh::NCList::BuildIndex() const
|
||||
inv_index.emplace(slaves[i].index, std::make_pair(MeshIdType::SLAVE, i));
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(inv_index.size() > 0,
|
||||
"Empty inverse index, member lists must be populated before BuildIndex is called!");
|
||||
}
|
||||
|
||||
//// Neighbors /////////////////////////////////////////////////////////////////
|
||||
@@ -5429,21 +5260,12 @@ void NCMesh::GetElementFacesAttributes(int leaf_elem,
|
||||
face_attribs[i] = face->attribute;
|
||||
}
|
||||
}
|
||||
|
||||
void NCMesh::FindFaceNodes(int face, int node[4]) const
|
||||
{
|
||||
auto tmp = FindFaceNodes(face);
|
||||
std::copy(tmp.begin(), tmp.end(), node);
|
||||
}
|
||||
|
||||
std::array<int, 4> NCMesh::FindFaceNodes(int face) const
|
||||
{
|
||||
return FindFaceNodes(faces[face]);
|
||||
}
|
||||
|
||||
std::array<int, 4> NCMesh::FindFaceNodes(const Face &fa) const
|
||||
{
|
||||
// Obtain face nodes from one of its elements (note that face->p1, p2, p3
|
||||
// cannot be used directly since they are not in order and p4 is missing).
|
||||
const Face &fa = faces[face];
|
||||
int elem = fa.elem[0];
|
||||
if (elem < 0) { elem = fa.elem[1]; }
|
||||
MFEM_ASSERT(elem >= 0, "Face has no elements?");
|
||||
@@ -5455,12 +5277,10 @@ std::array<int, 4> NCMesh::FindFaceNodes(const Face &fa) const
|
||||
find_node(el, fa.p3));
|
||||
|
||||
const int* fv = GI[el.Geom()].faces[f];
|
||||
std::array<int, 4> node;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
node[i] = el.node[fv[i]];
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
@@ -5474,11 +5294,13 @@ void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
if (Dim == 3)
|
||||
{
|
||||
GetFaceList(); // make sure 'boundary_faces' is up to date
|
||||
|
||||
for (int f : boundary_faces)
|
||||
{
|
||||
if (bdr_attr_is_ess[faces[f].attribute - 1])
|
||||
{
|
||||
auto node = FindFaceNodes(f);
|
||||
int node[4];
|
||||
FindFaceNodes(f, node);
|
||||
int nfv = (node[3] < 0) ? 3 : 4;
|
||||
|
||||
for (int j = 0; j < nfv; j++)
|
||||
@@ -5512,7 +5334,6 @@ void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
}
|
||||
else if (Dim == 2)
|
||||
{
|
||||
GetFaceList();
|
||||
GetEdgeList(); // make sure 'boundary_faces' is up to date
|
||||
|
||||
for (int f : boundary_faces)
|
||||
@@ -5733,7 +5554,9 @@ void NCMesh::LimitNCLevel(int max_nc_level)
|
||||
{
|
||||
Array<Refinement> refinements;
|
||||
GetLimitRefinements(refinements, max_nc_level);
|
||||
|
||||
if (!refinements.Size()) { break; }
|
||||
|
||||
Refine(refinements);
|
||||
}
|
||||
}
|
||||
@@ -6024,15 +5847,12 @@ void NCMesh::InitRootElements()
|
||||
|
||||
// count the root elements
|
||||
int nroots = 0;
|
||||
for (const auto &e : elements)
|
||||
if (e.parent == -1)
|
||||
{
|
||||
++nroots;
|
||||
}
|
||||
MFEM_VERIFY(nroots > 0 ||
|
||||
elements.Size() == 0,
|
||||
"invalid mesh file: no root elements in non-empty mesh found.");
|
||||
|
||||
while (nroots < elements.Size() &&
|
||||
elements[nroots].parent == -1)
|
||||
{
|
||||
nroots++;
|
||||
}
|
||||
MFEM_VERIFY(nroots, "invalid mesh file: no root elements found.");
|
||||
|
||||
// check that only the first 'nroot' elements are roots (have no parent)
|
||||
for (int i = nroots; i < elements.Size(); i++)
|
||||
@@ -6072,9 +5892,6 @@ NCMesh::NCMesh(std::istream &input, int version, int &curved, int &is_nc)
|
||||
std::string ident;
|
||||
int count;
|
||||
|
||||
// Skip the version string
|
||||
skip_comment_lines(input, 'M');
|
||||
|
||||
// load dimension
|
||||
skip_comment_lines(input, '#');
|
||||
input >> ident;
|
||||
@@ -6201,10 +6018,9 @@ NCMesh::NCMesh(std::istream &input, int version, int &curved, int &is_nc)
|
||||
{
|
||||
LoadCoordinates(input);
|
||||
|
||||
MFEM_VERIFY(coordinates.Size() >= 3*CountTopLevelNodes(),
|
||||
MFEM_VERIFY(coordinates.Size()/3 >= CountTopLevelNodes(),
|
||||
"Invalid mesh file: not all top-level nodes are covered by "
|
||||
"the 'coordinates' section of the mesh file: " << coordinates.Size() << ' ' <<
|
||||
3*CountTopLevelNodes());
|
||||
"the 'coordinates' section of the mesh file.");
|
||||
curved = 0;
|
||||
}
|
||||
else if (ident == "nodes")
|
||||
@@ -6266,7 +6082,7 @@ void NCMesh::LoadCoarseElements(std::istream &input)
|
||||
int ref_type;
|
||||
input >> ref_type;
|
||||
|
||||
int elem = AddElement(Geometry::INVALID, 0);
|
||||
int elem = AddElement(Element(Geometry::INVALID, 0));
|
||||
Element &el = elements[elem];
|
||||
el.ref_type = ref_type;
|
||||
|
||||
@@ -6353,7 +6169,7 @@ void NCMesh::LoadLegacyFormat(std::istream &input, int &curved, int &is_nc)
|
||||
CheckSupportedGeom(type);
|
||||
GI[geom].InitGeom(type);
|
||||
|
||||
int eid = AddElement(type, attr);
|
||||
int eid = AddElement(Element(type, attr));
|
||||
MFEM_ASSERT(eid == i, "");
|
||||
|
||||
Element &el = elements[eid];
|
||||
|
||||
+119
-222
@@ -29,10 +29,10 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** Represents the index of an element to refine, plus a refinement type. The
|
||||
refinement type is needed for anisotropic refinement of quads and hexes.
|
||||
Bits 0,1 and 2 of 'ref_type' specify whether the element should be split in
|
||||
the X, Y and Z directions, respectively (Z is ignored for quads). */
|
||||
/** Represents the index of an element to refine, plus a refinement type.
|
||||
The refinement type is needed for anisotropic refinement of quads and hexes.
|
||||
Bits 0,1 and 2 of 'ref_type' specify whether the element should be split
|
||||
in the X, Y and Z directions, respectively (Z is ignored for quads). */
|
||||
struct Refinement
|
||||
{
|
||||
enum : char { X = 1, Y = 2, Z = 4, XY = 3, XZ = 5, YZ = 6, XYZ = 7 };
|
||||
@@ -45,6 +45,7 @@ struct Refinement
|
||||
: index(index), ref_type(type) {}
|
||||
};
|
||||
|
||||
|
||||
/// Defines the position of a fine element within a coarse element.
|
||||
struct Embedding
|
||||
{
|
||||
@@ -53,8 +54,7 @@ struct Embedding
|
||||
|
||||
/** The (geom, matrix) pair determines the sub-element transformation for the
|
||||
fine element: CoarseFineTransformations::point_matrices[geom](matrix) is
|
||||
the point matrix of the region within the coarse element reference
|
||||
domain.*/
|
||||
the point matrix of the region within the coarse element reference domain.*/
|
||||
unsigned geom : 4;
|
||||
unsigned matrix : 27;
|
||||
|
||||
@@ -66,6 +66,7 @@ struct Embedding
|
||||
: parent(elem), geom(geom), matrix(matrix), ghost(ghost) {}
|
||||
};
|
||||
|
||||
|
||||
/// Defines the coarse-fine transformations of all fine elements.
|
||||
struct CoarseFineTransformations
|
||||
{
|
||||
@@ -95,23 +96,24 @@ void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b);
|
||||
|
||||
struct MatrixMap; // for internal use
|
||||
|
||||
/** \brief A class for non-conforming AMR. The class is not used directly by the
|
||||
* user, rather it is an extension of the Mesh class.
|
||||
|
||||
/** \brief A class for non-conforming AMR. The class is not used directly
|
||||
* by the user, rather it is an extension of the Mesh class.
|
||||
*
|
||||
* In general, the class is used by MFEM as follows:
|
||||
*
|
||||
* 1. NCMesh is constructed from elements of an existing Mesh. The elements are
|
||||
* copied and become roots of the refinement hierarchy.
|
||||
* 1. NCMesh is constructed from elements of an existing Mesh. The elements
|
||||
* are copied and become roots of the refinement hierarchy.
|
||||
*
|
||||
* 2. Some elements are refined with the Refine() method. Both isotropic and
|
||||
* anisotropic refinements of quads/hexes are supported.
|
||||
*
|
||||
* 3. A new Mesh is created from NCMesh containing the leaf elements. This new
|
||||
* Mesh may have non-conforming (hanging) edges and faces and is the one
|
||||
* seen by the user.
|
||||
* 3. A new Mesh is created from NCMesh containing the leaf elements.
|
||||
* This new Mesh may have non-conforming (hanging) edges and faces and
|
||||
* is the one seen by the user.
|
||||
*
|
||||
* 4. FiniteElementSpace asks NCMesh for a list of conforming, master and slave
|
||||
* edges/faces and creates the conforming interpolation matrix P.
|
||||
* 4. FiniteElementSpace asks NCMesh for a list of conforming, master and
|
||||
* slave edges/faces and creates the conforming interpolation matrix P.
|
||||
*
|
||||
* 5. A continuous/conforming solution is obtained by solving P'*A*P x = P'*b.
|
||||
*
|
||||
@@ -119,10 +121,8 @@ struct MatrixMap; // for internal use
|
||||
*/
|
||||
class NCMesh
|
||||
{
|
||||
protected:
|
||||
NCMesh() = default;
|
||||
public:
|
||||
//// Initialize with elements from an existing Mesh.
|
||||
//// Initialize with elements from an existing 'mesh'.
|
||||
explicit NCMesh(const Mesh *mesh);
|
||||
|
||||
/** Load from a stream. The id header is assumed to have been read already
|
||||
@@ -155,8 +155,8 @@ public:
|
||||
virtual int GetNGhostElements() const { return 0; }
|
||||
|
||||
/** Perform the given batch of refinements. Please note that in the presence
|
||||
of anisotropic splits additional refinements may be necessary to keep the
|
||||
mesh consistent. However, the function always performs at least the
|
||||
of anisotropic splits additional refinements may be necessary to keep
|
||||
the mesh consistent. However, the function always performs at least the
|
||||
requested refinements. */
|
||||
virtual void Refine(const Array<Refinement> &refinements);
|
||||
|
||||
@@ -172,16 +172,14 @@ public:
|
||||
const Table &GetDerefinementTable();
|
||||
|
||||
/** Check derefinements returned by GetDerefinementTable and mark those that
|
||||
can be done safely so that the maximum NC level condition is not
|
||||
violated. On return, level_ok.Size() == deref_table.Size() and contains
|
||||
0/1s. */
|
||||
can be done safely so that the maximum NC level condition is not violated.
|
||||
On return, level_ok.Size() == deref_table.Size() and contains 0/1s. */
|
||||
virtual void CheckDerefinementNCLevel(const Table &deref_table,
|
||||
Array<int> &level_ok, int max_nc_level);
|
||||
|
||||
/** Perform a subset of the possible derefinements (see
|
||||
GetDerefinementTable). Note that if anisotropic refinements are present
|
||||
in the mesh, some of the derefinements may have to be skipped to preserve
|
||||
mesh consistency. */
|
||||
/** Perform a subset of the possible derefinements (see GetDerefinementTable).
|
||||
Note that if anisotropic refinements are present in the mesh, some of the
|
||||
derefinements may have to be skipped to preserve mesh consistency. */
|
||||
virtual void Derefine(const Array<int> &derefs);
|
||||
|
||||
// master/slave lists
|
||||
@@ -342,9 +340,9 @@ public:
|
||||
const CoarseFineTransformations& GetRefinementTransforms() const;
|
||||
|
||||
/** After derefinement, calculate the relations of previous fine elements
|
||||
(some of which may no longer exist) to the current leaf elements. Unlike
|
||||
for refinement, Derefine() may only be called once before this function
|
||||
so there is no MarkFineLevel(). */
|
||||
(some of which may no longer exist) to the current leaf elements.
|
||||
Unlike for refinement, Derefine() may only be called once before this
|
||||
function so there is no MarkFineLevel(). */
|
||||
const CoarseFineTransformations& GetDerefinementTransforms() const;
|
||||
|
||||
/// Free all internal data created by the above three functions.
|
||||
@@ -361,8 +359,8 @@ public:
|
||||
static void GridSfcOrdering2D(int width, int height,
|
||||
Array<int> &coords);
|
||||
|
||||
/** Return a space filling curve for a 3D rectangular grid of elements. The
|
||||
Hilbert-curve-like algorithm works well for even dimensions. For odd
|
||||
/** Return a space filling curve for a 3D rectangular grid of elements.
|
||||
The Hilbert-curve-like algorithm works well for even dimensions. For odd
|
||||
width/height/depth it tends to produce some diagonal (edge-neighbor)
|
||||
steps. Even dimensions are recommended. */
|
||||
static void GridSfcOrdering3D(int width, int height, int depth,
|
||||
@@ -430,20 +428,17 @@ public:
|
||||
/// Return the number of root elements.
|
||||
int GetNumRootElements() { return root_state.Size(); }
|
||||
|
||||
/// Return the distance of leaf @a i from the root.
|
||||
/// Return the distance of leaf 'i' from the root.
|
||||
int GetElementDepth(int i) const;
|
||||
|
||||
/** Return the size reduction compared to the root element (ignoring local
|
||||
stretching and curvature). */
|
||||
int GetElementSizeReduction(int i) const;
|
||||
|
||||
/// Return the faces and face attributes of leaf element @a i.
|
||||
/// Return the faces and face attributes of leaf element 'i'.
|
||||
void GetElementFacesAttributes(int i, Array<int> &faces,
|
||||
Array<int> &fattr) const;
|
||||
|
||||
/// Set the attribute of leaf element @a i, which is a Mesh element index.
|
||||
void SetAttribute(int i, int attr)
|
||||
{ elements[leaf_elements[i]].attribute = attr; }
|
||||
|
||||
/** I/O: Print the mesh in "MFEM NC mesh v1.0" format. If @a comments is
|
||||
non-empty, it will be printed after the first line of the file, and each
|
||||
@@ -464,26 +459,8 @@ public:
|
||||
|
||||
int PrintMemoryDetail() const;
|
||||
|
||||
using RefCoord = std::int64_t;
|
||||
typedef std::int64_t RefCoord;
|
||||
|
||||
static constexpr int MaxElemNodes =
|
||||
8; ///< Number of nodes an element can have
|
||||
static constexpr int MaxElemEdges =
|
||||
12; ///< Number of edges an element can have
|
||||
static constexpr int MaxElemFaces =
|
||||
6; ///< Number of faces an element can have
|
||||
static constexpr int MaxElemChildren =
|
||||
10; ///< Number of children an element can have
|
||||
static constexpr int MaxFaceNodes =
|
||||
4; ///< Number of faces an element can have
|
||||
|
||||
/**
|
||||
* @brief Given a node index, return the vertex index associated
|
||||
*
|
||||
* @param node
|
||||
* @return int
|
||||
*/
|
||||
int GetNodeVertex(int node) { return nodes[node].vert_index; }
|
||||
|
||||
protected: // non-public interface for the Mesh class
|
||||
|
||||
@@ -496,8 +473,8 @@ protected: // non-public interface for the Mesh class
|
||||
Face::index) after a new mesh was created from us. */
|
||||
void OnMeshUpdated(Mesh *mesh);
|
||||
|
||||
/** Delete top-level vertex coordinates if the Mesh became curved, e.g., by
|
||||
calling Mesh::SetCurvature or otherwise setting the Nodes. */
|
||||
/** Delete top-level vertex coordinates if the Mesh became curved, e.g.,
|
||||
by calling Mesh::SetCurvature or otherwise setting the Nodes. */
|
||||
void MakeTopologyOnly() { coordinates.DeleteAll(); }
|
||||
|
||||
protected: // implementation
|
||||
@@ -508,15 +485,23 @@ protected: // implementation
|
||||
int Geoms; ///< bit mask of element geometries present, see InitGeomFlags()
|
||||
bool Legacy; ///< true if the mesh was loaded from the legacy v1.1 format
|
||||
|
||||
static const int MaxElemNodes =
|
||||
8; ///< Number of nodes of an element can have
|
||||
static const int MaxElemEdges =
|
||||
12; ///< Number of edges of an element can have
|
||||
static const int MaxElemFaces =
|
||||
6; ///< Number of faces of an element can have
|
||||
static const int MaxElemChildren =
|
||||
10; ///< Number of children of an element can have
|
||||
|
||||
/** A Node can hold a vertex, an edge, or both. Elements directly point to
|
||||
their corner nodes, but edge nodes also exist and can be accessed using a
|
||||
hash-table given their two end-point node IDs. All nodes can be accessed
|
||||
in this way, with the exception of top-level vertex nodes. When an
|
||||
element is being refined, the mid-edge nodes are readily available with
|
||||
this mechanism. The new elements "sign in" to the nodes by increasing the
|
||||
reference counts of their vertices and edges. The parent element "signs
|
||||
off" its nodes by decrementing the ref counts. */
|
||||
their corner nodes, but edge nodes also exist and can be accessed using
|
||||
a hash-table given their two end-point node IDs. All nodes can be
|
||||
accessed in this way, with the exception of top-level vertex nodes.
|
||||
When an element is being refined, the mid-edge nodes are readily
|
||||
available with this mechanism. The new elements "sign in" to the nodes
|
||||
by increasing the reference counts of their vertices and edges. The
|
||||
parent element "signs off" its nodes by decrementing the ref counts. */
|
||||
struct Node : public Hashed2
|
||||
{
|
||||
char vert_refc, edge_refc;
|
||||
@@ -534,9 +519,9 @@ protected: // implementation
|
||||
};
|
||||
|
||||
/** Similarly to nodes, faces can be accessed by hashing their four vertex
|
||||
node IDs. A face knows about the one or two elements that are using it. A
|
||||
face that is not on the boundary and only has one element referencing it
|
||||
is either a master or a slave face. */
|
||||
node IDs. A face knows about the one or two elements that are using it.
|
||||
A face that is not on the boundary and only has one element referencing
|
||||
it is either a master or a slave face. */
|
||||
struct Face : public Hashed4
|
||||
{
|
||||
int attribute; ///< boundary element attribute, -1 if internal face
|
||||
@@ -554,12 +539,11 @@ protected: // implementation
|
||||
|
||||
/// Return one of elem[0] or elem[1] and make sure the other is -1.
|
||||
int GetSingleElement() const;
|
||||
int GetAttribute() const { return attribute; }
|
||||
};
|
||||
|
||||
/** This is an element in the refinement hierarchy. Each element has either
|
||||
been refined and points to its children, or is a leaf and points to its
|
||||
vertex nodes. */
|
||||
/** This is an element in the refinement hierarchy. Each element has
|
||||
either been refined and points to its children, or is a leaf and points
|
||||
to its vertex nodes. */
|
||||
struct Element
|
||||
{
|
||||
char geom; ///< Geometry::Type of the element (char for storage only)
|
||||
@@ -575,114 +559,46 @@ protected: // implementation
|
||||
int child[MaxElemChildren]; ///< 2-10 children (if ref_type != 0)
|
||||
};
|
||||
int parent; ///< parent element, -1 if this is a root element, -2 if free'd
|
||||
|
||||
Element(Geometry::Type geom, int attr);
|
||||
|
||||
Geometry::Type Geom() const { return Geometry::Type(geom); }
|
||||
bool IsLeaf() const { return !ref_type && (parent != -2); }
|
||||
int GetAttribute() const { return attribute; }
|
||||
};
|
||||
|
||||
|
||||
// primary data
|
||||
|
||||
HashTable<Node> nodes; // associative container holding all Nodes
|
||||
HashTable<Face> faces; // associative container holding all Faces
|
||||
|
||||
BlockArray<Element> elements; // storage for all Elements
|
||||
Array<int> free_element_ids; // unused element ids - indices into 'elements'
|
||||
public:
|
||||
/**
|
||||
* @brief The number of Nodes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int GetNumNodes() const { return nodes.Size(); }
|
||||
/**
|
||||
* @brief Access a Node
|
||||
*
|
||||
* @param i Index of the node
|
||||
* @return const Node&
|
||||
*/
|
||||
const Node& GetNode(int i) const {return nodes[i]; }
|
||||
/**
|
||||
* @brief The number of faces
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int GetNumFaces() const { return faces.Size(); }
|
||||
/**
|
||||
* @brief Access a Face
|
||||
*
|
||||
* @param i Index of the face
|
||||
* @return const Face&
|
||||
*/
|
||||
const Face& GetFace(int i) const {return faces[i]; }
|
||||
/**
|
||||
* @brief The number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int GetNumElements() const { return elements.Size(); }
|
||||
/**
|
||||
* @brief Access an Element
|
||||
*
|
||||
* @param i Index of the element
|
||||
* @return const Element&
|
||||
*/
|
||||
const Element& GetElement(int i) const { return elements[i]; }
|
||||
|
||||
/**
|
||||
* @brief Given a set of nodes defining a face, traverse the nodes structure
|
||||
* to find the nodes that make up the parent face and replace the input nodes
|
||||
* with the parent nodes. Additionally return the child index that the child
|
||||
* face would be, relative to the discovered parent face.
|
||||
* @details This method is concerned with the construction of an NCMesh
|
||||
* structure for a d-1 manifold of an existing NCMesh. It forms a key element
|
||||
* in a leaf -> root traversal of the parent ncmesh elements structure.
|
||||
*
|
||||
* @param[out] nodes The collection of nodes whose parent we are searching
|
||||
* for
|
||||
* @return int The child index corresponding to placing the face for the
|
||||
* original nodes within the face defined by the returned parent nodes. If
|
||||
* child index is -1, then the face is made up of root nodes, and nodes is
|
||||
* unchanged.
|
||||
*/
|
||||
int ParentFaceNodes(std::array<int, 4> &nodes) const;
|
||||
|
||||
/**
|
||||
* @brief Method for finding the nodes associated to a @a face
|
||||
* @return Nodes making up the face
|
||||
*/
|
||||
std::array<int, 4> FindFaceNodes(int face) const;
|
||||
std::array<int, 4> FindFaceNodes(const Face &fa) const;
|
||||
/**
|
||||
* @brief Backwards compatible method for finding the @a node associated to a
|
||||
* @a face
|
||||
*/
|
||||
MFEM_DEPRECATED void FindFaceNodes(int face, int node[4]) const;
|
||||
protected:
|
||||
|
||||
/** Initial traversal state (~ element orientation) for each root element
|
||||
NOTE: M = root_state.Size() is the number of root elements. NOTE: the
|
||||
first M items of 'elements' is the coarse mesh. */
|
||||
NOTE: M = root_state.Size() is the number of root elements.
|
||||
NOTE: the first M items of 'elements' is the coarse mesh. */
|
||||
Array<int> root_state;
|
||||
|
||||
/** Coordinates of top-level vertices (organized as triples). If empty, the
|
||||
Mesh is curved (Nodes != NULL) and NCMesh is topology-only. */
|
||||
/** Coordinates of top-level vertices (organized as triples). If empty,
|
||||
the Mesh is curved (Nodes != NULL) and NCMesh is topology-only. */
|
||||
Array<real_t> coordinates;
|
||||
|
||||
|
||||
// secondary data
|
||||
|
||||
/** Apart from the primary data structure, which is the element/node/face
|
||||
hierarchy, there is secondary data that is derived from the primary data
|
||||
and needs to be updated when the primary data changes. Update() takes
|
||||
care of that and needs to be called after each refinement and
|
||||
hierarchy, there is secondary data that is derived from the primary
|
||||
data and needs to be updated when the primary data changes. Update()
|
||||
takes care of that and needs to be called after each refinement and
|
||||
derefinement. */
|
||||
virtual void Update();
|
||||
|
||||
// set by UpdateLeafElements, UpdateVertices and OnMeshUpdated
|
||||
int NElements, NVertices, NEdges, NFaces;
|
||||
|
||||
// NOTE: the serial code understands the bare minimum about ghost elements
|
||||
// and other ghost entities in order to be able to load parallel partial
|
||||
// meshes
|
||||
// NOTE: the serial code understands the bare minimum about ghost elements and
|
||||
// other ghost entities in order to be able to load parallel partial meshes
|
||||
int NGhostElements, NGhostVertices, NGhostEdges, NGhostFaces;
|
||||
|
||||
Array<int> leaf_elements; ///< finest elements, in Mesh ordering (+ ghosts)
|
||||
@@ -707,19 +623,19 @@ protected:
|
||||
We must be careful to:
|
||||
1. Stay compatible with the conforming code, which expects top-level
|
||||
(original) vertices to be indexed first, otherwise GridFunctions
|
||||
defined on a conforming mesh would no longer be valid when the mesh is
|
||||
converted to an NC mesh.
|
||||
defined on a conforming mesh would no longer be valid when the
|
||||
mesh is converted to an NC mesh.
|
||||
|
||||
2. Make sure serial NCMesh is compatible with the parallel ParNCMesh, so
|
||||
it is possible to read parallel partial solutions in serial code
|
||||
2. Make sure serial NCMesh is compatible with the parallel ParNCMesh,
|
||||
so it is possible to read parallel partial solutions in serial code
|
||||
(e.g., serial GLVis). This means handling ghost elements, if present.
|
||||
|
||||
3. Assign vertices in a globally consistent order for parallel meshes: if
|
||||
two vertices i,j are shared by two ranks r1,r2, and i<j on r1, then
|
||||
i<j on r2 as well. This is true for top-level vertices but also for
|
||||
the remaining shared vertices thanks to the globally consistent SFC
|
||||
ordering of the leaf elements. This property reduces communication and
|
||||
simplifies ParNCMesh. */
|
||||
3. Assign vertices in a globally consistent order for parallel meshes:
|
||||
if two vertices i,j are shared by two ranks r1,r2, and i<j on r1,
|
||||
then i<j on r2 as well. This is true for top-level vertices but also
|
||||
for the remaining shared vertices thanks to the globally consistent
|
||||
SFC ordering of the leaf elements. This property reduces communication
|
||||
and simplifies ParNCMesh. */
|
||||
void UpdateVertices(); ///< update Vertex::index and vertex_nodeId
|
||||
|
||||
/** Collect the leaf elements in leaf_elements, and the ghost elements in
|
||||
@@ -730,8 +646,8 @@ protected:
|
||||
int &counter);
|
||||
|
||||
/** Try to find a space-filling curve friendly orientation of the root
|
||||
elements: set 'root_state' based on the ordering of coarse elements. Note
|
||||
that the coarse mesh itself must be ordered as an SFC by e.g.
|
||||
elements: set 'root_state' based on the ordering of coarse elements.
|
||||
Note that the coarse mesh itself must be ordered as an SFC by e.g.
|
||||
Mesh::GetGeckoElementOrdering. */
|
||||
void InitRootState(int root_count);
|
||||
|
||||
@@ -751,6 +667,7 @@ protected:
|
||||
/// Return true if the Element @a el is a ghost element.
|
||||
bool IsGhost(const Element &el) const { return el.rank != MyRank; }
|
||||
|
||||
|
||||
// refinement/derefinement
|
||||
|
||||
Array<Refinement> ref_stack; ///< stack of scheduled refinements (temporary)
|
||||
@@ -759,8 +676,8 @@ protected:
|
||||
|
||||
Table derefinements; ///< possible derefinements, see GetDerefinementTable
|
||||
|
||||
/** Refine the element @a elem with the refinement @a ref_type (c.f.
|
||||
Refinement::enum) */
|
||||
/** Refine the element @a elem with the refinement @a ref_type
|
||||
(c.f. Refinement::enum) */
|
||||
void RefineElement(int elem, char ref_type);
|
||||
|
||||
/// Derefine the element @a elem, does nothing on leaf elements.
|
||||
@@ -778,7 +695,6 @@ protected:
|
||||
}
|
||||
return elements.Append(el);
|
||||
}
|
||||
int AddElement(Geometry::Type geom, int attr) { return AddElement(Element(geom,attr)); }
|
||||
|
||||
// Free the element with index @a id.
|
||||
void FreeElement(int id)
|
||||
@@ -910,11 +826,6 @@ protected:
|
||||
|
||||
int GetMidFaceNode(int en1, int en2, int en3, int en4);
|
||||
|
||||
/**
|
||||
* @brief Add references to all nodes, edges and faces of the element
|
||||
*
|
||||
* @param elem index into elements
|
||||
*/
|
||||
void ReferenceElement(int elem);
|
||||
void UnreferenceElement(int elem, Array<int> &elemFaces);
|
||||
|
||||
@@ -971,28 +882,28 @@ protected:
|
||||
|
||||
// neighbors / element_vertex table
|
||||
|
||||
/** Return all vertex-, edge- and face-neighbors of a set of elements. The
|
||||
neighbors are returned as a list (neighbors != NULL), as a set
|
||||
/** Return all vertex-, edge- and face-neighbors of a set of elements.
|
||||
The neighbors are returned as a list (neighbors != NULL), as a set
|
||||
(neighbor_set != NULL), or both. The sizes of the set arrays must match
|
||||
that of leaf_elements. The function is intended to be used for large sets
|
||||
of elements and its complexity is linear in the number of leaf elements
|
||||
in the mesh. */
|
||||
that of leaf_elements. The function is intended to be used for large
|
||||
sets of elements and its complexity is linear in the number of leaf
|
||||
elements in the mesh. */
|
||||
void FindSetNeighbors(const Array<char> &elem_set,
|
||||
Array<int> *neighbors, /* append */
|
||||
Array<char> *neighbor_set = NULL);
|
||||
|
||||
/** Return all vertex-, edge- and face-neighbors of a single element. You can
|
||||
limit the number of elements being checked using 'search_set'. The
|
||||
complexity of the function is linear in the size of the search set.*/
|
||||
/** Return all vertex-, edge- and face-neighbors of a single element.
|
||||
You can limit the number of elements being checked using 'search_set'.
|
||||
The complexity of the function is linear in the size of the search set.*/
|
||||
void FindNeighbors(int elem,
|
||||
Array<int> &neighbors, /* append */
|
||||
const Array<int> *search_set = NULL);
|
||||
|
||||
/** Expand a set of elements by all vertex-, edge- and face-neighbors. The
|
||||
output array 'expanded' will contain all items from 'elems' (provided
|
||||
they are in 'search_set') plus their neighbors. The neighbor search can
|
||||
be limited to the optional search set. The complexity is linear in the
|
||||
sum of the sizes of 'elems' and 'search_set'. */
|
||||
/** Expand a set of elements by all vertex-, edge- and face-neighbors.
|
||||
The output array 'expanded' will contain all items from 'elems'
|
||||
(provided they are in 'search_set') plus their neighbors. The neighbor
|
||||
search can be limited to the optional search set. The complexity is
|
||||
linear in the sum of the sizes of 'elems' and 'search_set'. */
|
||||
void NeighborExpand(const Array<int> &elems,
|
||||
Array<int> &expanded,
|
||||
const Array<int> *search_set = NULL);
|
||||
@@ -1070,17 +981,18 @@ protected:
|
||||
/** @brief The PointMatrix stores the coordinates of the slave face using the
|
||||
master face coordinate as reference.
|
||||
|
||||
In 2D, the point matrix has the orientation of the parent edge, so its
|
||||
columns need to be flipped when applying it, see
|
||||
In 2D, the point matrix has the orientation of the parent
|
||||
edge, so its columns need to be flipped when applying it, see
|
||||
ApplyLocalSlaveTransformation.
|
||||
|
||||
In 3D, the orientation part of Elem2Inf is encoded in the point matrix.
|
||||
In 3D, the orientation part of Elem2Inf is encoded in the point
|
||||
matrix.
|
||||
|
||||
The following transformation gives the relation between the reference
|
||||
quad face coordinates (xi, eta) in [0,1]^2, and the fine quad face
|
||||
coordinates (x, y):
|
||||
x = a0*(1-xi)*(1-eta) + a1*xi*(1-eta) + a2*xi*eta + a3*(1-xi)*eta
|
||||
y = b0*(1-xi)*(1-eta) + b1*xi*(1-eta) + b2*xi*eta + b3*(1-xi)*eta
|
||||
The following transformation gives the relation between the
|
||||
reference quad face coordinates (xi, eta) in [0,1]^2, and the fine quad
|
||||
face coordinates (x, y):
|
||||
x = a0*(1-xi)*(1-eta) + a1*xi*(1-eta) + a2*xi*eta + a3*(1-xi)*eta
|
||||
y = b0*(1-xi)*(1-eta) + b1*xi*(1-eta) + b2*xi*eta + b3*(1-xi)*eta
|
||||
*/
|
||||
struct PointMatrix
|
||||
{
|
||||
@@ -1142,7 +1054,7 @@ protected:
|
||||
void GetPointMatrix(Geometry::Type geom, const char* ref_path,
|
||||
DenseMatrix& matrix) const;
|
||||
|
||||
using RefPathMap = std::map<std::string, int>;
|
||||
typedef std::map<std::string, int> RefPathMap;
|
||||
|
||||
void TraverseRefinements(int elem, int coarse_index,
|
||||
std::string &ref_path, RefPathMap &map) const;
|
||||
@@ -1173,15 +1085,15 @@ protected:
|
||||
|
||||
int GetEdgeMaster(int node) const;
|
||||
|
||||
void FindFaceNodes(int face, int node[4]) const;
|
||||
|
||||
/**
|
||||
* @brief Return the number of splits of this edge that have occurred in the
|
||||
* NCMesh. If zero, this means the segment is not the master of any other
|
||||
* segments.
|
||||
* NCMesh. If zero, this means the segment is not the master of any other segments.
|
||||
*
|
||||
* @param vn1 The first vertex making up the segment
|
||||
* @param vn2 The second vertex making up the segment
|
||||
* @return int The depth of splits of this segment that are present in the
|
||||
* mesh.
|
||||
* @return int The depth of splits of this segment that are present in the mesh.
|
||||
*/
|
||||
int EdgeSplitLevel(int vn1, int vn2) const;
|
||||
/**
|
||||
@@ -1192,14 +1104,13 @@ protected:
|
||||
* @param vn1 The first vertex making up the triangle
|
||||
* @param vn2 The second vertex making up the triangle
|
||||
* @param vn3 The third vertex making up the triangle
|
||||
* @return int The depth of splits of this triangle that are present in the
|
||||
* mesh.
|
||||
* @return int The depth of splits of this triangle that are present in the mesh.
|
||||
*/
|
||||
int TriFaceSplitLevel(int vn1, int vn2, int vn3) const;
|
||||
/**
|
||||
* @brief Computes the number of horizontal and vertical splits of this quad
|
||||
* that have occurred in the NCMesh. If zero, this means the quad is not the
|
||||
* master of any other quad.
|
||||
* that have occurred in the NCMesh. If zero, this means the quad is not
|
||||
* the master of any other quad.
|
||||
*
|
||||
* @param vn1 The first vertex making up the quad
|
||||
* @param vn2 The second vertex making up the quad
|
||||
@@ -1212,8 +1123,8 @@ protected:
|
||||
int& h_level, int& v_level) const;
|
||||
/**
|
||||
* @brief Returns the total number of splits of this quad that have occurred
|
||||
* in the NCMesh. If zero, this means the quad is not the master of any other
|
||||
* quad.
|
||||
* in the NCMesh. If zero, this means the quad is not
|
||||
* the master of any other quad.
|
||||
* @details This is a convenience wrapper that sums the horizontal and
|
||||
* vertical levels from the full method.
|
||||
*
|
||||
@@ -1230,17 +1141,6 @@ protected:
|
||||
void CountSplits(int elem, int splits[3]) const;
|
||||
void GetLimitRefinements(Array<Refinement> &refinements, int max_level);
|
||||
|
||||
// Checker helpers
|
||||
|
||||
static void CheckSupportedGeom(Geometry::Type geom)
|
||||
{
|
||||
MFEM_VERIFY(geom == Geometry::SEGMENT ||
|
||||
geom == Geometry::TRIANGLE || geom == Geometry::SQUARE ||
|
||||
geom == Geometry::CUBE || geom == Geometry::PRISM ||
|
||||
geom == Geometry::PYRAMID || geom == Geometry::TETRAHEDRON,
|
||||
"Element type " << geom << " is not supported by NCMesh.");
|
||||
}
|
||||
|
||||
|
||||
// I/O
|
||||
|
||||
@@ -1249,8 +1149,8 @@ protected:
|
||||
/// Load the vertex parent hierarchy from a mesh file.
|
||||
void LoadVertexParents(std::istream &input);
|
||||
|
||||
/** Print the "boundary" section of the mesh file. If out == NULL, only
|
||||
return the number of boundary elements. */
|
||||
/** Print the "boundary" section of the mesh file.
|
||||
If out == NULL, only return the number of boundary elements. */
|
||||
int PrintBoundary(std::ostream *out) const;
|
||||
/// Load the "boundary" section of the mesh file.
|
||||
void LoadBoundary(std::istream &input);
|
||||
@@ -1285,7 +1185,6 @@ protected:
|
||||
|
||||
bool initialized;
|
||||
GeomInfo() : initialized(false) {}
|
||||
GeomInfo(Geometry::Type geom) : GeomInfo() { InitGeom(geom); }
|
||||
void InitGeom(Geometry::Type geom);
|
||||
};
|
||||
|
||||
@@ -1300,8 +1199,6 @@ public:
|
||||
friend class ParNCMesh; // for ParNCMesh::ElementSet
|
||||
friend struct MatrixMap;
|
||||
friend struct PointMatrixHash;
|
||||
friend class NCSubMesh; // for faces, nodes
|
||||
friend class ParNCSubMesh; // for faces, nodes
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+68
-65
@@ -9,13 +9,14 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_NCMESH_TABLES
|
||||
#define MFEM_NCMESH_TABLES
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
static constexpr int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
|
||||
namespace // make everything static
|
||||
{
|
||||
|
||||
const int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
|
||||
|
||||
|
||||
// derefinement tables
|
||||
// The first n numbers in each line are the refined elements that contain
|
||||
@@ -23,14 +24,14 @@ static constexpr int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
|
||||
// are the refined elements that contain the faces attributes of the parent
|
||||
// element.
|
||||
|
||||
static constexpr int quad_deref_table[3][4 + 4] =
|
||||
const int quad_deref_table[3][4 + 4] =
|
||||
{
|
||||
{ 0, 1, 1, 0, /**/ 1, 1, 0, 0 }, // 1 - X
|
||||
{ 0, 0, 1, 1, /**/ 0, 0, 1, 1 }, // 2 - Y
|
||||
{ 0, 1, 2, 3, /**/ 1, 1, 3, 3 } // 3 - iso
|
||||
};
|
||||
|
||||
static constexpr int hex_deref_table[7][8 + 6] =
|
||||
const int hex_deref_table[7][8 + 6] =
|
||||
{
|
||||
{ 0, 1, 1, 0, 0, 1, 1, 0, /**/ 1, 1, 1, 0, 0, 0 }, // 1 - X
|
||||
{ 0, 0, 1, 1, 0, 0, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 2 - Y
|
||||
@@ -41,7 +42,7 @@ static constexpr int hex_deref_table[7][8 + 6] =
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, /**/ 1, 1, 1, 7, 7, 7 } // 7 - iso
|
||||
};
|
||||
|
||||
static constexpr int prism_deref_table[7][6 + 5] =
|
||||
const int prism_deref_table[7][6 + 5] =
|
||||
{
|
||||
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 1
|
||||
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 2
|
||||
@@ -52,7 +53,7 @@ static constexpr int prism_deref_table[7][6 + 5] =
|
||||
{ 0, 1, 2, 4, 5, 6, /**/ 0, 5, 0, 5, 0 } // 7 - iso
|
||||
};
|
||||
|
||||
static constexpr int pyramid_deref_table[7][5 + 5] =
|
||||
const int pyramid_deref_table[7][5 + 5] =
|
||||
{
|
||||
{-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 1
|
||||
{-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 2
|
||||
@@ -65,19 +66,19 @@ static constexpr int pyramid_deref_table[7][5 + 5] =
|
||||
|
||||
// child ordering tables
|
||||
|
||||
static constexpr char quad_hilbert_child_order[8][4] =
|
||||
const char quad_hilbert_child_order[8][4] =
|
||||
{
|
||||
{0,1,2,3}, {0,3,2,1}, {1,2,3,0}, {1,0,3,2},
|
||||
{2,3,0,1}, {2,1,0,3}, {3,0,1,2}, {3,2,1,0}
|
||||
};
|
||||
|
||||
static constexpr char quad_hilbert_child_state[8][4] =
|
||||
const char quad_hilbert_child_state[8][4] =
|
||||
{
|
||||
{1,0,0,5}, {0,1,1,4}, {3,2,2,7}, {2,3,3,6},
|
||||
{5,4,4,1}, {4,5,5,0}, {7,6,6,3}, {6,7,7,2}
|
||||
};
|
||||
|
||||
static constexpr char hex_hilbert_child_order[24][8] =
|
||||
const char hex_hilbert_child_order[24][8] =
|
||||
{
|
||||
{0,1,2,3,7,6,5,4}, {0,3,7,4,5,6,2,1}, {0,4,5,1,2,6,7,3},
|
||||
{1,0,3,2,6,7,4,5}, {1,2,6,5,4,7,3,0}, {1,5,4,0,3,7,6,2},
|
||||
@@ -89,7 +90,7 @@ static constexpr char hex_hilbert_child_order[24][8] =
|
||||
{7,3,2,6,5,1,0,4}, {7,4,0,3,2,1,5,6}, {7,6,5,4,0,1,2,3}
|
||||
};
|
||||
|
||||
static constexpr char hex_hilbert_child_state[24][8] =
|
||||
const char hex_hilbert_child_state[24][8] =
|
||||
{
|
||||
{1,2,2,7,7,21,21,17}, {2,0,0,22,22,16,16,8}, {0,1,1,15,15,6,6,23},
|
||||
{4,5,5,10,10,18,18,14}, {5,3,3,19,19,13,13,11}, {3,4,4,12,12,9,9,20},
|
||||
@@ -103,26 +104,27 @@ static constexpr char hex_hilbert_child_state[24][8] =
|
||||
|
||||
|
||||
// child/parent reference domain transforms
|
||||
using RefCoord = NCMesh::RefCoord;
|
||||
|
||||
typedef NCMesh::RefCoord RefCoord;
|
||||
|
||||
// reference domain coordinates as fixed point numbers
|
||||
static constexpr RefCoord T_HALF = (1ll << 59);
|
||||
static constexpr RefCoord T_ONE = (1ll << 60);
|
||||
static constexpr RefCoord T_TWO = (1ll << 61);
|
||||
const RefCoord T_HALF = (1ll << 59);
|
||||
const RefCoord T_ONE = (1ll << 60);
|
||||
const RefCoord T_TWO = (1ll << 61);
|
||||
|
||||
// (scaling factors have a different fixed point multiplier)
|
||||
static constexpr RefCoord S_HALF = 1;
|
||||
static constexpr RefCoord S_ONE = 2;
|
||||
static constexpr RefCoord S_TWO = 4;
|
||||
const RefCoord S_HALF = 1;
|
||||
const RefCoord S_ONE = 2;
|
||||
const RefCoord S_TWO = 4;
|
||||
|
||||
static constexpr RefCoord tri_corners[3][3] =
|
||||
const RefCoord tri_corners[3][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
{ 0, T_ONE, 0}
|
||||
};
|
||||
|
||||
static constexpr RefCoord quad_corners[4][3] =
|
||||
const RefCoord quad_corners[4][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -130,7 +132,7 @@ static constexpr RefCoord quad_corners[4][3] =
|
||||
{ 0, T_ONE, 0}
|
||||
};
|
||||
|
||||
static constexpr RefCoord hex_corners[8][3] =
|
||||
const RefCoord hex_corners[8][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -142,7 +144,7 @@ static constexpr RefCoord hex_corners[8][3] =
|
||||
{ 0, T_ONE, T_ONE}
|
||||
};
|
||||
|
||||
static constexpr RefCoord prism_corners[6][3] =
|
||||
const RefCoord prism_corners[6][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -152,7 +154,7 @@ static constexpr RefCoord prism_corners[6][3] =
|
||||
{ 0, T_ONE, T_ONE}
|
||||
};
|
||||
|
||||
static constexpr RefCoord pyramid_corners[5][3] =
|
||||
const RefCoord pyramid_corners[5][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -162,7 +164,7 @@ static constexpr RefCoord pyramid_corners[5][3] =
|
||||
};
|
||||
|
||||
typedef RefCoord RefPoint[3];
|
||||
static const RefPoint* geom_corners[8] =
|
||||
const RefPoint* geom_corners[8] =
|
||||
{
|
||||
NULL, // point
|
||||
NULL, // segment
|
||||
@@ -188,31 +190,31 @@ struct RefTrf
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr RefTrf quad_parent_rt1[2] =
|
||||
const RefTrf quad_parent_rt1[2] =
|
||||
{
|
||||
{ {S_HALF, S_ONE, 0}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_ONE, 0}, {T_HALF, 0, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf quad_child_rt1[2] =
|
||||
const RefTrf quad_child_rt1[2] =
|
||||
{
|
||||
{ {S_TWO, S_ONE, 0}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_ONE, 0}, {-T_ONE, 0, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf quad_parent_rt2[2] =
|
||||
const RefTrf quad_parent_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_HALF, 0}, {0, 0, 0} },
|
||||
{ {S_ONE, S_HALF, 0}, {0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf quad_child_rt2[2] =
|
||||
const RefTrf quad_child_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_TWO, 0}, {0, 0, 0} },
|
||||
{ {S_ONE, S_TWO, 0}, {0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf quad_parent_rt3[4] =
|
||||
const RefTrf quad_parent_rt3[4] =
|
||||
{
|
||||
{ {S_HALF, S_HALF, 0}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_HALF, 0}, {T_HALF, 0, 0} },
|
||||
@@ -220,7 +222,7 @@ static constexpr RefTrf quad_parent_rt3[4] =
|
||||
{ {S_HALF, S_HALF, 0}, { 0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf quad_child_rt3[4] =
|
||||
const RefTrf quad_child_rt3[4] =
|
||||
{
|
||||
{ {S_TWO, S_TWO, 0}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_TWO, 0}, {-T_ONE, 0, 0} },
|
||||
@@ -228,7 +230,7 @@ static constexpr RefTrf quad_child_rt3[4] =
|
||||
{ {S_TWO, S_TWO, 0}, { 0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
static const RefTrf* quad_parent[4] =
|
||||
const RefTrf* quad_parent[4] =
|
||||
{
|
||||
NULL,
|
||||
quad_parent_rt1,
|
||||
@@ -236,7 +238,7 @@ static const RefTrf* quad_parent[4] =
|
||||
quad_parent_rt3
|
||||
};
|
||||
|
||||
static const RefTrf* quad_child[4] =
|
||||
const RefTrf* quad_child[4] =
|
||||
{
|
||||
NULL,
|
||||
quad_child_rt1,
|
||||
@@ -244,31 +246,31 @@ static const RefTrf* quad_child[4] =
|
||||
quad_child_rt3
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt1[2] =
|
||||
const RefTrf hex_parent_rt1[2] =
|
||||
{
|
||||
{ {S_HALF, S_ONE, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_ONE, S_ONE}, {T_HALF, 0, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt1[2] =
|
||||
const RefTrf hex_child_rt1[2] =
|
||||
{
|
||||
{ {S_TWO, S_ONE, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_ONE, S_ONE}, {-T_ONE, 0, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt2[2] =
|
||||
const RefTrf hex_parent_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_HALF, S_ONE}, {0, 0, 0} },
|
||||
{ {S_ONE, S_HALF, S_ONE}, {0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt2[2] =
|
||||
const RefTrf hex_child_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_TWO, S_ONE}, {0, 0, 0} },
|
||||
{ {S_ONE, S_TWO, S_ONE}, {0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt3[4] =
|
||||
const RefTrf hex_parent_rt3[4] =
|
||||
{
|
||||
{ {S_HALF, S_HALF, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_HALF, S_ONE}, {T_HALF, 0, 0} },
|
||||
@@ -276,7 +278,7 @@ static constexpr RefTrf hex_parent_rt3[4] =
|
||||
{ {S_HALF, S_HALF, S_ONE}, { 0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt3[4] =
|
||||
const RefTrf hex_child_rt3[4] =
|
||||
{
|
||||
{ {S_TWO, S_TWO, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_TWO, S_ONE}, {-T_ONE, 0, 0} },
|
||||
@@ -284,19 +286,19 @@ static constexpr RefTrf hex_child_rt3[4] =
|
||||
{ {S_TWO, S_TWO, S_ONE}, { 0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt4[2] =
|
||||
const RefTrf hex_parent_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, T_HALF} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt4[2] =
|
||||
const RefTrf hex_child_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, -T_ONE} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt5[4] =
|
||||
const RefTrf hex_parent_rt5[4] =
|
||||
{
|
||||
{ {S_HALF, S_ONE, S_HALF}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_ONE, S_HALF}, {T_HALF, 0, 0} },
|
||||
@@ -304,7 +306,7 @@ static constexpr RefTrf hex_parent_rt5[4] =
|
||||
{ {S_HALF, S_ONE, S_HALF}, { 0, 0, T_HALF} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt5[4] =
|
||||
const RefTrf hex_child_rt5[4] =
|
||||
{
|
||||
{ {S_TWO, S_ONE, S_TWO}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_ONE, S_TWO}, {-T_ONE, 0, 0} },
|
||||
@@ -312,7 +314,7 @@ static constexpr RefTrf hex_child_rt5[4] =
|
||||
{ {S_TWO, S_ONE, S_TWO}, { 0, 0, -T_ONE} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt6[4] =
|
||||
const RefTrf hex_parent_rt6[4] =
|
||||
{
|
||||
{ {S_ONE, S_HALF, S_HALF}, {0, 0, 0} },
|
||||
{ {S_ONE, S_HALF, S_HALF}, {0, T_HALF, 0} },
|
||||
@@ -320,7 +322,7 @@ static constexpr RefTrf hex_parent_rt6[4] =
|
||||
{ {S_ONE, S_HALF, S_HALF}, {0, T_HALF, T_HALF} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt6[4] =
|
||||
const RefTrf hex_child_rt6[4] =
|
||||
{
|
||||
{ {S_ONE, S_TWO, S_TWO}, {0, 0, 0} },
|
||||
{ {S_ONE, S_TWO, S_TWO}, {0, -T_ONE, 0} },
|
||||
@@ -328,7 +330,7 @@ static constexpr RefTrf hex_child_rt6[4] =
|
||||
{ {S_ONE, S_TWO, S_TWO}, {0, -T_ONE, -T_ONE} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_parent_rt7[8] =
|
||||
const RefTrf hex_parent_rt7[8] =
|
||||
{
|
||||
{ {S_HALF, S_HALF, S_HALF}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_HALF, S_HALF}, {T_HALF, 0, 0} },
|
||||
@@ -340,7 +342,7 @@ static constexpr RefTrf hex_parent_rt7[8] =
|
||||
{ {S_HALF, S_HALF, S_HALF}, { 0, T_HALF, T_HALF} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf hex_child_rt7[8] =
|
||||
const RefTrf hex_child_rt7[8] =
|
||||
{
|
||||
{ {S_TWO, S_TWO, S_TWO}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, 0} },
|
||||
@@ -352,7 +354,7 @@ static constexpr RefTrf hex_child_rt7[8] =
|
||||
{ {S_TWO, S_TWO, S_TWO}, { 0, -T_ONE, -T_ONE} }
|
||||
};
|
||||
|
||||
static const RefTrf* hex_parent[8] =
|
||||
const RefTrf* hex_parent[8] =
|
||||
{
|
||||
NULL,
|
||||
hex_parent_rt1,
|
||||
@@ -364,7 +366,7 @@ static const RefTrf* hex_parent[8] =
|
||||
hex_parent_rt7
|
||||
};
|
||||
|
||||
static const RefTrf* hex_child[8] =
|
||||
const RefTrf* hex_child[8] =
|
||||
{
|
||||
NULL,
|
||||
hex_child_rt1,
|
||||
@@ -376,7 +378,7 @@ static const RefTrf* hex_child[8] =
|
||||
hex_child_rt7
|
||||
};
|
||||
|
||||
static constexpr RefTrf tri_parent_rt3[4] =
|
||||
const RefTrf tri_parent_rt3[4] =
|
||||
{
|
||||
{ { S_HALF, S_HALF, 0}, { 0, 0, 0} },
|
||||
{ { S_HALF, S_HALF, 0}, {T_HALF, 0, 0} },
|
||||
@@ -384,7 +386,7 @@ static constexpr RefTrf tri_parent_rt3[4] =
|
||||
{ {-S_HALF, -S_HALF, 0}, {T_HALF, T_HALF, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf tri_child_rt3[4] =
|
||||
const RefTrf tri_child_rt3[4] =
|
||||
{
|
||||
{ { S_TWO, S_TWO, 0}, { 0, 0, 0} },
|
||||
{ { S_TWO, S_TWO, 0}, {-T_ONE, 0, 0} },
|
||||
@@ -392,19 +394,19 @@ static constexpr RefTrf tri_child_rt3[4] =
|
||||
{ {-S_TWO, -S_TWO, 0}, { T_ONE, T_ONE, 0} }
|
||||
};
|
||||
|
||||
static const RefTrf* tri_parent[4] =
|
||||
const RefTrf* tri_parent[4] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
tri_parent_rt3
|
||||
};
|
||||
|
||||
static const RefTrf* tri_child[4] =
|
||||
const RefTrf* tri_child[4] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
tri_child_rt3
|
||||
};
|
||||
|
||||
static constexpr RefTrf prism_parent_rt3[4] =
|
||||
const RefTrf prism_parent_rt3[4] =
|
||||
{
|
||||
{ { S_HALF, S_HALF, S_ONE}, { 0, 0, 0} },
|
||||
{ { S_HALF, S_HALF, S_ONE}, {T_HALF, 0, 0} },
|
||||
@@ -412,7 +414,7 @@ static constexpr RefTrf prism_parent_rt3[4] =
|
||||
{ {-S_HALF, -S_HALF, S_ONE}, {T_HALF, T_HALF, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf prism_child_rt3[4] =
|
||||
const RefTrf prism_child_rt3[4] =
|
||||
{
|
||||
{ { S_TWO, S_TWO, S_ONE}, { 0, 0, 0} },
|
||||
{ { S_TWO, S_TWO, S_ONE}, {-T_ONE, 0, 0} },
|
||||
@@ -420,19 +422,19 @@ static constexpr RefTrf prism_child_rt3[4] =
|
||||
{ {-S_TWO, -S_TWO, S_ONE}, { T_ONE, T_ONE, 0} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf prism_parent_rt4[2] =
|
||||
const RefTrf prism_parent_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, T_HALF} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf prism_child_rt4[2] =
|
||||
const RefTrf prism_child_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, -T_ONE} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf prism_parent_rt7[8] =
|
||||
const RefTrf prism_parent_rt7[8] =
|
||||
{
|
||||
{ { S_HALF, S_HALF, S_HALF}, { 0, 0, 0} },
|
||||
{ { S_HALF, S_HALF, S_HALF}, {T_HALF, 0, 0} },
|
||||
@@ -444,7 +446,7 @@ static constexpr RefTrf prism_parent_rt7[8] =
|
||||
{ {-S_HALF, -S_HALF, S_HALF}, {T_HALF, T_HALF, T_HALF} }
|
||||
};
|
||||
|
||||
static constexpr RefTrf prism_child_rt7[8] =
|
||||
const RefTrf prism_child_rt7[8] =
|
||||
{
|
||||
{ { S_TWO, S_TWO, S_TWO}, { 0, 0, 0} },
|
||||
{ { S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, 0} },
|
||||
@@ -456,7 +458,7 @@ static constexpr RefTrf prism_child_rt7[8] =
|
||||
{ {-S_TWO, -S_TWO, S_TWO}, { T_ONE, T_ONE, -T_ONE} }
|
||||
};
|
||||
|
||||
static const RefTrf* prism_parent[8] =
|
||||
const RefTrf* prism_parent[8] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
prism_parent_rt3,
|
||||
@@ -465,7 +467,7 @@ static const RefTrf* prism_parent[8] =
|
||||
prism_parent_rt7
|
||||
};
|
||||
|
||||
static const RefTrf* prism_child[8] =
|
||||
const RefTrf* prism_child[8] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
prism_child_rt3,
|
||||
@@ -474,7 +476,7 @@ static const RefTrf* prism_child[8] =
|
||||
prism_child_rt7
|
||||
};
|
||||
|
||||
static const RefTrf** geom_parent[7] =
|
||||
const RefTrf** geom_parent[7] =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -485,7 +487,7 @@ static const RefTrf** geom_parent[7] =
|
||||
prism_parent
|
||||
};
|
||||
|
||||
static const RefTrf** geom_child[7] =
|
||||
const RefTrf** geom_child[7] =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -496,6 +498,7 @@ static const RefTrf** geom_child[7] =
|
||||
prism_child
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_NCMESH_TABLES
|
||||
} // namespace
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -5602,6 +5602,7 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
|
||||
}
|
||||
|
||||
MPI_Barrier(MyComm);
|
||||
serialmesh.SetAttributes();
|
||||
return serialmesh;
|
||||
}
|
||||
|
||||
|
||||
+38
-65
@@ -109,8 +109,8 @@ protected:
|
||||
// Determine sedge_ledge and sface_lface.
|
||||
void FinalizeParTopo();
|
||||
|
||||
// Mark all tets to ensure consistency across MPI tasks; also mark the shared
|
||||
// and boundary triangle faces using the consistently marked tets.
|
||||
// Mark all tets to ensure consistency across MPI tasks; also mark the
|
||||
// shared and boundary triangle faces using the consistently marked tets.
|
||||
void MarkTetMeshForRefinement(const DSTable &v_to_v) override;
|
||||
|
||||
/// Return a number(0-1) identifying how the given edge has been split
|
||||
@@ -337,12 +337,12 @@ public:
|
||||
have_face_nbr_data(false), pncmesh(NULL) { }
|
||||
|
||||
/// Create a parallel mesh by partitioning a serial Mesh.
|
||||
/** The mesh is partitioned automatically or using external partitioning data
|
||||
(the optional parameter 'partitioning_[i]' contains the desired MPI rank
|
||||
for element 'i'). Automatic partitioning uses METIS for conforming meshes
|
||||
and quick space-filling curve equipartitioning for nonconforming meshes
|
||||
(elements of nonconforming meshes should ideally be ordered as a sequence
|
||||
of face-neighbors). */
|
||||
/** The mesh is partitioned automatically or using external partitioning
|
||||
data (the optional parameter 'partitioning_[i]' contains the desired MPI
|
||||
rank for element 'i'). Automatic partitioning uses METIS for conforming
|
||||
meshes and quick space-filling curve equipartitioning for nonconforming
|
||||
meshes (elements of nonconforming meshes should ideally be ordered as a
|
||||
sequence of face-neighbors). */
|
||||
ParMesh(MPI_Comm comm, Mesh &mesh, const int *partitioning_ = nullptr,
|
||||
int part_method = 1);
|
||||
|
||||
@@ -446,42 +446,11 @@ public:
|
||||
int GroupNTriangles(int group) const { return group_stria.RowSize(group-1); }
|
||||
int GroupNQuadrilaterals(int group) const { return group_squad.RowSize(group-1); }
|
||||
|
||||
/**
|
||||
* @brief Accessors for entities within a shared group structure.
|
||||
* @details For all vertex/edge/face the two argument version returns the
|
||||
* local index, for those entities with an orientation. The two out parameter
|
||||
* version additionally returns an orientation to use in manipulating the
|
||||
* entity.
|
||||
*
|
||||
* @param group The communicator group's indices
|
||||
* @param i the index within the group
|
||||
* @return int The local index of the entity
|
||||
*/
|
||||
int GroupVertex(int group, int i) const
|
||||
{ return svert_lvert[group_svert.GetRow(group-1)[i]]; }
|
||||
void GroupEdge(int group, int i, int &edge, int &o) const;
|
||||
void GroupTriangle(int group, int i, int &face, int &o) const;
|
||||
void GroupQuadrilateral(int group, int i, int &face, int &o) const;
|
||||
int GroupEdge(int group, int i) const
|
||||
{
|
||||
int e, o;
|
||||
GroupEdge(group, i, e, o);
|
||||
return e;
|
||||
}
|
||||
int GroupTriangle(int group, int i) const
|
||||
{
|
||||
int f, o;
|
||||
GroupTriangle(group, i, f, o);
|
||||
return f;
|
||||
}
|
||||
int GroupQuadrilateral(int group, int i) const
|
||||
{
|
||||
int f, o;
|
||||
GroupQuadrilateral(group, i, f, o);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
@@ -527,15 +496,18 @@ public:
|
||||
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[],
|
||||
Array<HYPRE_BigInt> *offsets[]) const;
|
||||
|
||||
using Mesh::FaceIsTrueInterior;
|
||||
/** Return true if the face is interior or shared. In parallel, this
|
||||
method only works if the face neighbor data is exchanged. */
|
||||
inline bool FaceIsTrueInterior(int FaceNo) const { return Mesh::FaceIsTrueInterior(FaceNo); }
|
||||
|
||||
void ExchangeFaceNbrData();
|
||||
void ExchangeFaceNbrNodes();
|
||||
|
||||
void SetCurvature(int order, bool discont = false, int space_dim = -1,
|
||||
int ordering = 1) override;
|
||||
|
||||
/** Replace the internal node GridFunction with a new GridFunction defined on
|
||||
the given FiniteElementSpace. The new node coordinates are projected
|
||||
/** Replace the internal node GridFunction with a new GridFunction defined
|
||||
on the given FiniteElementSpace. The new node coordinates are projected
|
||||
(derived) from the current nodes/vertices. */
|
||||
void SetNodalFESpace(FiniteElementSpace *nfes) override;
|
||||
void SetNodalFESpace(ParFiniteElementSpace *npfes);
|
||||
@@ -599,15 +571,15 @@ public:
|
||||
IsoparametricTransformation &ElTr2,
|
||||
int mask = 31) const override;
|
||||
|
||||
/// @brief Get the FaceElementTransformations for the given shared face (edge
|
||||
/// 2D) using the shared face index @a sf. @a fill2 specify if the
|
||||
/// information for elem2 of the face should be computed or not. In the
|
||||
/// returned object, 1 and 2 refer to the local and the neighbor elements,
|
||||
/// respectively.
|
||||
/// @brief Get the FaceElementTransformations for the given shared face
|
||||
/// (edge 2D) using the shared face index @a sf. @a fill2 specify if the
|
||||
/// information for elem2 of the face should be computed or not.
|
||||
/// In the returned object, 1 and 2 refer to the local and the neighbor
|
||||
/// elements, respectively.
|
||||
///
|
||||
/// @note The returned object is owned by the class and is shared, i.e.,
|
||||
/// calling this function resets pointers obtained from previous calls. Also,
|
||||
/// the returned object should NOT be deleted by the caller.
|
||||
/// calling this function resets pointers obtained from previous calls.
|
||||
/// Also, the returned object should NOT be deleted by the caller.
|
||||
FaceElementTransformations *
|
||||
GetSharedFaceTransformations(int sf, bool fill2 = true);
|
||||
|
||||
@@ -619,14 +591,15 @@ public:
|
||||
IsoparametricTransformation &ElTr2,
|
||||
bool fill2 = true) const;
|
||||
|
||||
/// @brief Get the FaceElementTransformations for the given shared face (edge
|
||||
/// 2D) using the face index @a FaceNo. @a fill2 specify if the information
|
||||
/// for elem2 of the face should be computed or not. In the returned object,
|
||||
/// 1 and 2 refer to the local and the neighbor elements, respectively.
|
||||
/// @brief Get the FaceElementTransformations for the given shared face
|
||||
/// (edge 2D) using the face index @a FaceNo. @a fill2 specify if the
|
||||
/// information for elem2 of the face should be computed or not.
|
||||
/// In the returned object, 1 and 2 refer to the local and the neighbor
|
||||
/// elements, respectively.
|
||||
///
|
||||
/// @note The returned object is owned by the class and is shared, i.e.,
|
||||
/// calling this function resets pointers obtained from previous calls. Also,
|
||||
/// the returned object should NOT be deleted by the caller.
|
||||
/// calling this function resets pointers obtained from previous calls.
|
||||
/// Also, the returned object should NOT be deleted by the caller.
|
||||
FaceElementTransformations *
|
||||
GetSharedFaceTransformationsByLocalIndex(int FaceNo, bool fill2 = true);
|
||||
|
||||
@@ -642,8 +615,8 @@ public:
|
||||
/// neighbor.
|
||||
///
|
||||
/// @note The returned object is owned by the class and is shared, i.e.,
|
||||
/// calling this function resets pointers obtained from previous calls. Also,
|
||||
/// the returned object should NOT be deleted by the caller.
|
||||
/// calling this function resets pointers obtained from previous calls.
|
||||
/// Also, the returned object should NOT be deleted by the caller.
|
||||
ElementTransformation *GetFaceNbrElementTransformation(int FaceNo);
|
||||
|
||||
/// @brief Variant of GetFaceNbrElementTransformation using a user allocated
|
||||
@@ -664,11 +637,11 @@ public:
|
||||
/** @brief Returns the number of local faces according to the requested type,
|
||||
does not count master non-conforming faces.
|
||||
|
||||
If type==Boundary returns only the number of true boundary faces contrary
|
||||
to GetNBE() that returns all "boundary" elements which may include actual
|
||||
interior faces. Similarly, if type==Interior, only the true interior
|
||||
faces (including shared faces) are counted excluding all master
|
||||
non-conforming faces. */
|
||||
If type==Boundary returns only the number of true boundary faces
|
||||
contrary to GetNBE() that returns all "boundary" elements which may
|
||||
include actual interior faces.
|
||||
Similarly, if type==Interior, only the true interior faces (including
|
||||
shared faces) are counted excluding all master non-conforming faces. */
|
||||
int GetNFbyType(FaceType type) const override;
|
||||
|
||||
void GenerateBoundaryElements() override
|
||||
@@ -684,9 +657,9 @@ public:
|
||||
sequence of elements. Works for nonconforming meshes only. */
|
||||
void Rebalance();
|
||||
|
||||
/** Load balance a nonconforming mesh using a user-defined partition. Each
|
||||
local element 'i' is migrated to processor rank 'partition[i]', for 0 <=
|
||||
i < GetNE(). */
|
||||
/** Load balance a nonconforming mesh using a user-defined partition.
|
||||
Each local element 'i' is migrated to processor rank 'partition[i]',
|
||||
for 0 <= i < GetNE(). */
|
||||
void Rebalance(const Array<int> &partition);
|
||||
|
||||
/** Save the mesh in a parallel mesh format. If @a comments is non-empty, it
|
||||
|
||||
@@ -63,8 +63,6 @@ class FiniteElementSpace;
|
||||
*/
|
||||
class ParNCMesh : public NCMesh
|
||||
{
|
||||
protected:
|
||||
ParNCMesh() = default;
|
||||
public:
|
||||
/// Construct by partitioning a serial NCMesh.
|
||||
/** SFC partitioning is used by default. A user-specified partition can be
|
||||
@@ -254,7 +252,6 @@ public:
|
||||
protected: // interface for ParMesh
|
||||
|
||||
friend class ParMesh;
|
||||
friend class ParSubMesh;
|
||||
|
||||
/** For compatibility with conforming code in ParMesh and ParFESpace.
|
||||
Initializes shared structures in ParMesh: gtopo, shared_*, group_s*,
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
// Copyright (c) 2010-2024, 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 "ncsubmesh.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include "submesh_utils.hpp"
|
||||
#include "submesh.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace SubMeshUtils;
|
||||
|
||||
NCSubMesh::NCSubMesh(SubMesh& submesh, const NCMesh &parent, From from,
|
||||
const Array<int> &attributes)
|
||||
: NCMesh(), parent_(&parent)
|
||||
{
|
||||
Dim = submesh.Dimension();
|
||||
spaceDim = submesh.SpaceDimension();
|
||||
MyRank = 0;
|
||||
Iso = true;
|
||||
Legacy = false;
|
||||
|
||||
if (from == From::Domain)
|
||||
{
|
||||
SubMeshUtils::ConstructVolumeTree(*this, attributes);
|
||||
}
|
||||
else if (from == From::Boundary)
|
||||
{
|
||||
SubMeshUtils::ConstructFaceTree(*this, attributes);
|
||||
}
|
||||
|
||||
// Loop over all nodes, and reparent based on the node relations of the
|
||||
// parent
|
||||
for (int i = 0; i < parent_node_ids_.Size(); i++)
|
||||
{
|
||||
const auto &parent_node = parent.nodes[parent_node_ids_[i]];
|
||||
const int submesh_p1 = parent_to_submesh_node_ids_[parent_node.p1];
|
||||
const int submesh_p2 = parent_to_submesh_node_ids_[parent_node.p2];
|
||||
nodes.Reparent(i, submesh_p1, submesh_p2);
|
||||
}
|
||||
|
||||
nodes.UpdateUnused();
|
||||
for (int i = 0; i < elements.Size(); i++)
|
||||
{
|
||||
if (elements[i].IsLeaf())
|
||||
{
|
||||
// Register all faces
|
||||
RegisterFaces(i);
|
||||
}
|
||||
}
|
||||
|
||||
InitRootElements();
|
||||
InitRootState(root_state.Size());
|
||||
InitGeomFlags();
|
||||
Update(); // Fills in secondary information based off of elements, nodes and faces.
|
||||
|
||||
// If parent has coordinates defined, copy the relevant portion
|
||||
if (parent.coordinates.Size() > 0)
|
||||
{
|
||||
coordinates.SetSize(3*parent_node_ids_.Size());
|
||||
parent.tmp_vertex = new TmpVertex[parent.nodes.NumIds()];
|
||||
for (int n = 0; n < parent_node_ids_.Size(); n++)
|
||||
{
|
||||
std::memcpy(&coordinates[3*n], parent.CalcVertexPos(parent_node_ids_[n]),
|
||||
3*sizeof(real_t));
|
||||
}
|
||||
delete [] parent.tmp_vertex;
|
||||
}
|
||||
|
||||
// The element indexing was changed as part of generation of leaf elements.
|
||||
// We need to update the map.
|
||||
if (from == From::Domain)
|
||||
{
|
||||
// The element indexing was changed as part of generation of leaf
|
||||
// elements. We need to update the map.
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
submesh.parent_element_ids_[i] =
|
||||
parent.elements[parent_element_ids_[leaf_elements[i]]].index;
|
||||
submesh.parent_to_submesh_element_ids_[submesh.parent_element_ids_[i]] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
// parent elements are BOUNDARY elements, need to map face index to be.
|
||||
const auto &parent_face_to_be = submesh.GetParent()->GetFaceToBdrElMap();
|
||||
MFEM_ASSERT(NElements == submesh.GetNE(), "!");
|
||||
auto new_parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
Array<int> new_parent_element_ids;
|
||||
new_parent_element_ids.Reserve(submesh.parent_element_ids_.Size());
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
new_parent_element_ids.Append(
|
||||
parent_face_to_be[parent.faces[parent_element_ids_[leaf_elements[i]]].index]);
|
||||
new_parent_to_submesh_element_ids[new_parent_element_ids[i]] = i;
|
||||
}
|
||||
|
||||
MFEM_ASSERT(new_parent_element_ids.Size() == submesh.parent_element_ids_.Size(),
|
||||
"!");
|
||||
#ifdef MFEM_DEBUG
|
||||
for (auto x : new_parent_element_ids)
|
||||
{
|
||||
MFEM_ASSERT(std::find(submesh.parent_element_ids_.begin(),
|
||||
submesh.parent_element_ids_.end(), x)
|
||||
!= submesh.parent_element_ids_.end(),
|
||||
x << " not found in submesh.parent_element_ids_");
|
||||
}
|
||||
for (auto x : submesh.parent_element_ids_)
|
||||
{
|
||||
MFEM_ASSERT(std::find(new_parent_element_ids.begin(),
|
||||
new_parent_element_ids.end(), x)
|
||||
!= new_parent_element_ids.end(), x << " not found in new_parent_element_ids_");
|
||||
}
|
||||
#endif
|
||||
submesh.parent_element_ids_ = std::move(new_parent_element_ids);
|
||||
submesh.parent_to_submesh_element_ids_ =
|
||||
std::move(new_parent_to_submesh_element_ids);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright (c) 2010-2024, 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_NCSUBMESH
|
||||
#define MFEM_NCSUBMESH
|
||||
|
||||
#include "../ncmesh.hpp"
|
||||
#include "submesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Class representing a Nonconformal SubMesh. This is only used by
|
||||
* SubMesh.
|
||||
*/
|
||||
class NCSubMesh : public NCMesh
|
||||
{
|
||||
friend class SubMesh; ///< Only SubMesh can use methods in this class
|
||||
public:
|
||||
using From = SubMesh::From; ///< Convenience type alias
|
||||
/// Get the parent NCMesh object
|
||||
const NCMesh* GetParent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if NCMesh @a m is a NCSubMesh.
|
||||
*
|
||||
* @param m The input NCMesh
|
||||
*/
|
||||
static bool IsNCSubMesh(const NCMesh *m)
|
||||
{
|
||||
return dynamic_cast<const NCSubMesh *>(m) != nullptr;
|
||||
}
|
||||
private:
|
||||
|
||||
/// Private constructor
|
||||
NCSubMesh(SubMesh& submesh, const NCMesh &parent, From from,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/// The parent NCMesh. Not owned.
|
||||
const NCMesh *parent_;
|
||||
|
||||
/// Mapping from submesh element nc ids (index of the array), to the parent
|
||||
/// element ids. If from a boundary, these map to faces in the parent.
|
||||
Array<int> parent_element_ids_;
|
||||
|
||||
/// Mapping from NCSubMesh node ids (index of the array), to the parent
|
||||
/// NCMesh node ids.
|
||||
Array<int> parent_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh node ids to submesh NCMesh node ids.
|
||||
// Inverse map of parent_node_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh element ids to submesh NCMesh element ids.
|
||||
// Inverse map of parent_element_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_element_ids_;
|
||||
|
||||
// Helper friend methods for construction.
|
||||
friend void SubMeshUtils::ConstructFaceTree<NCSubMesh>(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
friend void SubMeshUtils::ConstructVolumeTree<NCSubMesh>(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent nodes
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Node>&
|
||||
*/
|
||||
const HashTable<Node> &ParentNodes() const { return parent_->nodes; }
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent faces
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Face>&
|
||||
*/
|
||||
const HashTable<Face> &ParentFaces() const { return parent_->faces; }
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_NCSUBMESH
|
||||
@@ -1,157 +0,0 @@
|
||||
// Copyright (c) 2010-2024, 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 "../../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pncsubmesh.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include "submesh_utils.hpp"
|
||||
#include "psubmesh.hpp"
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace SubMeshUtils;
|
||||
|
||||
|
||||
ParNCSubMesh::ParNCSubMesh(ParSubMesh& submesh, const ParNCMesh &parent,
|
||||
From from, const Array<int> &attributes)
|
||||
: ParNCMesh(), parent_(&parent)
|
||||
{
|
||||
MyComm = submesh.GetComm();
|
||||
NRanks = submesh.GetNRanks();
|
||||
MyRank = submesh.GetMyRank();
|
||||
|
||||
Dim = submesh.Dimension();
|
||||
spaceDim = submesh.SpaceDimension();
|
||||
Iso = true;
|
||||
Legacy = false;
|
||||
|
||||
// Loop over parent leaf elements and add nodes for all vertices. Register as
|
||||
// top level nodes, will reparent when looping over edges. Cannot add edge
|
||||
// nodes at same time because top level vertex nodes must be contiguous and
|
||||
// first in node list (see coordinates).
|
||||
if (from == From::Domain)
|
||||
{
|
||||
SubMeshUtils::ConstructVolumeTree(*this, attributes);
|
||||
}
|
||||
else if (from == From::Boundary)
|
||||
{
|
||||
SubMeshUtils::ConstructFaceTree(*this, attributes);
|
||||
}
|
||||
|
||||
// Loop over all nodes, and reparent based on the node relations of the
|
||||
// parent
|
||||
for (int i = 0; i < parent_node_ids_.Size(); i++)
|
||||
{
|
||||
const auto &parent_node = parent.nodes[parent_node_ids_[i]];
|
||||
const int submesh_p1 = parent_to_submesh_node_ids_[parent_node.p1];
|
||||
const int submesh_p2 = parent_to_submesh_node_ids_[parent_node.p2];
|
||||
nodes.Reparent(i, submesh_p1, submesh_p2);
|
||||
}
|
||||
|
||||
nodes.UpdateUnused();
|
||||
for (int i = 0; i < elements.Size(); i++)
|
||||
{
|
||||
if (elements[i].IsLeaf())
|
||||
{
|
||||
// Register all faces
|
||||
RegisterFaces(i);
|
||||
}
|
||||
}
|
||||
|
||||
InitRootElements();
|
||||
InitRootState(root_state.Size());
|
||||
InitGeomFlags();
|
||||
Update(); // Fills in secondary information based off of elements, nodes and faces.
|
||||
#ifdef MFEM_DEBUG
|
||||
// Check all processors have the same number of roots
|
||||
{
|
||||
int p[2] = {root_state.Size(), -root_state.Size()};
|
||||
MPI_Allreduce(MPI_IN_PLACE, p, 2, MPI_INT, MPI_MIN, submesh.GetComm());
|
||||
MFEM_ASSERT(p[0] == -p[1], "Ranks must agree on number of root elements: min "
|
||||
<< p[0] << " max " << -p[1] << " local " << root_state.Size() << " MyRank " <<
|
||||
submesh.GetMyRank());
|
||||
}
|
||||
#endif
|
||||
|
||||
// If parent has coordinates defined, copy the relevant portion
|
||||
if (parent.coordinates.Size() > 0)
|
||||
{
|
||||
// Loop over new_nodes -> coordinates is indexed by node.
|
||||
coordinates.SetSize(3*parent_node_ids_.Size());
|
||||
parent.tmp_vertex = new TmpVertex[parent.nodes.NumIds()];
|
||||
for (int n = 0; n < parent_node_ids_.Size(); n++)
|
||||
{
|
||||
std::memcpy(&coordinates[3*n], parent.CalcVertexPos(parent_node_ids_[n]),
|
||||
3*sizeof(real_t));
|
||||
}
|
||||
delete [] parent.tmp_vertex;
|
||||
}
|
||||
|
||||
// The element indexing was changed as part of generation of leaf elements.
|
||||
// We need to update the map.
|
||||
if (from == From::Domain)
|
||||
{
|
||||
// The element indexing was changed as part of generation of leaf
|
||||
// elements. We need to update the map.
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
submesh.parent_element_ids_[i] =
|
||||
parent.elements[parent_element_ids_[leaf_elements[i]]].index;
|
||||
submesh.parent_to_submesh_element_ids_[submesh.parent_element_ids_[i]] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
// parent elements are BOUNDARY elements, need to map face index to be.
|
||||
const auto &parent_face_to_be = submesh.GetParent()->GetFaceToBdrElMap();
|
||||
MFEM_ASSERT(NElements == submesh.GetNE(), NElements << ' ' << submesh.GetNE());
|
||||
auto new_parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
Array<int> new_parent_element_ids;
|
||||
new_parent_element_ids.Reserve(submesh.parent_element_ids_.Size());
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
new_parent_element_ids.Append(
|
||||
parent_face_to_be[parent.faces[parent_element_ids_[leaf_elements[i]]].index]);
|
||||
new_parent_to_submesh_element_ids[new_parent_element_ids[i]] = i;
|
||||
}
|
||||
|
||||
MFEM_ASSERT(new_parent_element_ids.Size() == submesh.parent_element_ids_.Size(),
|
||||
new_parent_element_ids.Size() << ' ' << submesh.parent_element_ids_.Size());
|
||||
#ifdef MFEM_DEBUG
|
||||
for (auto x : new_parent_element_ids)
|
||||
{
|
||||
MFEM_ASSERT(std::find(submesh.parent_element_ids_.begin(),
|
||||
submesh.parent_element_ids_.end(), x)
|
||||
!= submesh.parent_element_ids_.end(),
|
||||
x << " not found in submesh.parent_element_ids_");
|
||||
}
|
||||
for (auto x : submesh.parent_element_ids_)
|
||||
{
|
||||
MFEM_ASSERT(std::find(new_parent_element_ids.begin(),
|
||||
new_parent_element_ids.end(), x)
|
||||
!= new_parent_element_ids.end(), x << " not found in new_parent_element_ids_");
|
||||
}
|
||||
#endif
|
||||
submesh.parent_element_ids_ = new_parent_element_ids;
|
||||
submesh.parent_to_submesh_element_ids_ = new_parent_to_submesh_element_ids;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) 2010-2024, 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_PNCSUBMESH
|
||||
#define MFEM_PNCSUBMESH
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "../pncmesh.hpp"
|
||||
#include "psubmesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Class representing a Parallel Nonconformal SubMesh. This is only used
|
||||
* by ParSubMesh.
|
||||
*/
|
||||
class ParNCSubMesh : public ParNCMesh
|
||||
{
|
||||
friend class ParSubMesh; ///< Only ParSubMesh can use methods in this class
|
||||
public:
|
||||
using From = SubMesh::From; ///< Convenience type alias
|
||||
/**
|
||||
* @brief Check if NCMesh @a m is a ParNCSubMesh.
|
||||
*
|
||||
* @param m The input Mesh
|
||||
*/
|
||||
static bool IsParNCSubMesh(const NCMesh *m)
|
||||
{
|
||||
return dynamic_cast<const ParNCSubMesh *>(m) != nullptr;
|
||||
}
|
||||
/// Get the parent ParNCMesh object
|
||||
const ParNCMesh* GetParent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// protected constructor
|
||||
ParNCSubMesh(ParSubMesh& submesh, const ParNCMesh &parent, From from,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/// The parent ParNCMesh. Not owned.
|
||||
const ParNCMesh *parent_;
|
||||
|
||||
/// Mapping from submesh element nc ids (index of the array), to the parent
|
||||
/// element ids. If from a boundary, these map to faces in the parent.
|
||||
Array<int> parent_element_ids_;
|
||||
|
||||
/// Mapping from ParNCSubMesh node ids (index of the array), to the parent
|
||||
/// NCMesh node ids.
|
||||
Array<int> parent_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh node ids to submesh NCMesh node ids.
|
||||
// Inverse map of parent_node_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh element ids to submesh NCMesh element ids.
|
||||
// Inverse map of parent_element_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_element_ids_;
|
||||
|
||||
// Helper friend methods for construction.
|
||||
friend void SubMeshUtils::ConstructFaceTree<ParNCSubMesh>
|
||||
(ParNCSubMesh &submesh, const Array<int> &attributes);
|
||||
friend void SubMeshUtils::ConstructVolumeTree<ParNCSubMesh>
|
||||
(ParNCSubMesh &submesh, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent nodes
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Node>&
|
||||
*/
|
||||
const HashTable<Node> &ParentNodes() const { return parent_->nodes; }
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent faces
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Face>&
|
||||
*/
|
||||
const HashTable<Face> &ParentFaces() const { return parent_->faces; }
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#endif // MFEM_PNCSUBMESH
|
||||
+333
-377
@@ -17,7 +17,6 @@
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include "psubmesh.hpp"
|
||||
#include "pncsubmesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include "../segment.hpp"
|
||||
|
||||
@@ -25,29 +24,33 @@ namespace mfem
|
||||
{
|
||||
|
||||
ParSubMesh ParSubMesh::CreateFromDomain(const ParMesh &parent,
|
||||
const Array<int> &domain_attributes)
|
||||
Array<int> &domain_attributes)
|
||||
{
|
||||
return ParSubMesh(parent, SubMesh::From::Domain, domain_attributes);
|
||||
}
|
||||
|
||||
ParSubMesh ParSubMesh::CreateFromBoundary(const ParMesh &parent,
|
||||
const Array<int> &boundary_attributes)
|
||||
Array<int> &boundary_attributes)
|
||||
{
|
||||
return ParSubMesh(parent, SubMesh::From::Boundary, boundary_attributes);
|
||||
}
|
||||
|
||||
ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
const Array<int> &attributes) : parent_(parent), from_(from),
|
||||
attributes_(attributes)
|
||||
Array<int> &attributes) : parent_(parent), from_(from), attributes_(attributes)
|
||||
{
|
||||
if (Nonconforming())
|
||||
{
|
||||
MFEM_ABORT("SubMesh does not support non-conforming meshes");
|
||||
}
|
||||
|
||||
MyComm = parent.GetComm();
|
||||
NRanks = parent.GetNRanks();
|
||||
MyRank = parent.GetMyRank();
|
||||
|
||||
// This violation of const-ness may be justified in this instance because the
|
||||
// exchange of face neighbor information only establishes or updates derived
|
||||
// information without altering the primary mesh information, i.e., the
|
||||
// topology, geometry, or region attributes.
|
||||
// This violation of const-ness may be justified in this instance because
|
||||
// the exchange of face neighbor information only establishes or updates
|
||||
// derived information without altering the primary mesh information,
|
||||
// i.e., the topology, geometry, or region attributes.
|
||||
const_cast<ParMesh&>(parent).ExchangeFaceNbrData();
|
||||
|
||||
if (from == SubMesh::From::Domain)
|
||||
@@ -67,48 +70,16 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
attributes_, true);
|
||||
}
|
||||
|
||||
parent_to_submesh_vertex_ids_.SetSize(parent_.GetNV());
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
|
||||
}
|
||||
|
||||
parent_to_submesh_element_ids_.SetSize(from == From::Boundary ? parent.GetNBE()
|
||||
: parent.GetNE());
|
||||
parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < parent_element_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_element_ids_[parent_element_ids_[i]] = i;
|
||||
}
|
||||
|
||||
// Don't let boundary elements get generated automatically. This would
|
||||
// generate boundary elements on each rank locally, which is topologically
|
||||
// wrong for the distributed SubMesh.
|
||||
FinalizeTopology(false);
|
||||
|
||||
if (parent.Nonconforming())
|
||||
parent_to_submesh_vertex_ids_.SetSize(parent_.GetNV());
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
pncmesh = new ParNCSubMesh(*this, *parent.pncmesh, from, attributes);
|
||||
pncsubmesh_ = dynamic_cast<ParNCSubMesh*>(pncmesh);
|
||||
ncmesh = pncmesh;
|
||||
InitFromNCMesh(*pncmesh);
|
||||
pncmesh->OnMeshUpdated(this);
|
||||
|
||||
// Update the submesh to parent vertex mapping, NCSubMesh reordered the
|
||||
// vertices so the map to parent is no longer valid.
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
// vertex -> node -> parent node -> parent vertex
|
||||
auto node = pncsubmesh_->vertex_nodeId[i];
|
||||
auto parent_node = pncsubmesh_->parent_node_ids_[node];
|
||||
auto parent_vertex = parent.pncmesh->GetNodeVertex(parent_node);
|
||||
parent_vertex_ids_[i] = parent_vertex;
|
||||
parent_to_submesh_vertex_ids_[parent_vertex] = i;
|
||||
}
|
||||
GenerateNCFaceInfo();
|
||||
SetAttributes();
|
||||
parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
|
||||
}
|
||||
|
||||
DSTable v2v(parent_.GetNV());
|
||||
@@ -144,6 +115,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
}
|
||||
|
||||
parent_face_ori_.SetSize(NumOfFaces);
|
||||
|
||||
for (int i = 0; i < NumOfFaces; i++)
|
||||
{
|
||||
Array<int> sub_vert;
|
||||
@@ -219,6 +191,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
// Every rank containing elements of the ParSubMesh attributes now has a
|
||||
// local ParSubMesh. We have to connect the local meshes and assign global
|
||||
// boundaries correctly.
|
||||
|
||||
Array<int> rhvtx;
|
||||
FindSharedVerticesRanks(rhvtx);
|
||||
AppendSharedVerticesGroups(groups, rhvtx);
|
||||
@@ -234,7 +207,6 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
AppendSharedFacesGroups(groups, rht, rhq);
|
||||
}
|
||||
|
||||
|
||||
// Build the group communication topology
|
||||
gtopo.SetComm(MyComm);
|
||||
gtopo.Create(groups, 822);
|
||||
@@ -267,17 +239,113 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
|
||||
ExchangeFaceNbrData();
|
||||
|
||||
SubMeshUtils::AddBoundaryElements(*this,
|
||||
(from == SubMesh::From::Domain)
|
||||
? FindGhostBoundaryElementAttributes()
|
||||
: std::unordered_map<int,int> {});
|
||||
|
||||
if (Dim > 1)
|
||||
// Add boundaries
|
||||
{
|
||||
if (!el_to_edge) { el_to_edge = new Table; }
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
const int num_codim_1 = [this]()
|
||||
{
|
||||
if (Dim == 1) { return NumOfVertices; }
|
||||
else if (Dim == 2) { return NumOfEdges; }
|
||||
else if (Dim == 3) { return NumOfFaces; }
|
||||
else { MFEM_ABORT("Invalid dimension."); return -1; }
|
||||
}();
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
// In 3D we check for `bel_to_edge`. It shouldn't have been set
|
||||
// previously.
|
||||
delete bel_to_edge;
|
||||
bel_to_edge = nullptr;
|
||||
}
|
||||
|
||||
NumOfBdrElements = 0;
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
NumOfBdrElements++;
|
||||
}
|
||||
}
|
||||
|
||||
boundary.SetSize(NumOfBdrElements);
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
|
||||
for (int i = 0, j = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
boundary[j] = faces[i]->Duplicate(this);
|
||||
be_to_face[j] = i;
|
||||
|
||||
if (from == SubMesh::From::Domain && Dim >= 2)
|
||||
{
|
||||
int pbeid = Dim == 3 ? parent_face_to_be[parent_face_ids_[i]] :
|
||||
parent_face_to_be[parent_edge_ids_[i]];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
boundary[j]->SetAttribute(parent.GetBdrAttribute(pbeid));
|
||||
}
|
||||
else
|
||||
{
|
||||
boundary[j]->SetAttribute(max_bdr_attr + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boundary[j]->SetAttribute(SubMesh::GENERATED_ATTRIBUTE);
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (from == SubMesh::From::Domain && Dim >= 2)
|
||||
{
|
||||
// Search for and count interior boundary elements
|
||||
int InteriorBdrElems = 0;
|
||||
for (int i=0; i<parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
Dim == 3 ?
|
||||
parent_to_submesh_face_ids_[parentFaceIdx] :
|
||||
parent_to_submesh_edge_ids_[parentFaceIdx];
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (GetFaceInformation(submeshFaceIdx).IsBoundary()) { continue; }
|
||||
|
||||
InteriorBdrElems++;
|
||||
}
|
||||
|
||||
if (InteriorBdrElems > 0)
|
||||
{
|
||||
const int OldNumOfBdrElements = NumOfBdrElements;
|
||||
NumOfBdrElements += InteriorBdrElems;
|
||||
boundary.SetSize(NumOfBdrElements);
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
|
||||
// Search for and transfer interior boundary elements
|
||||
for (int i=0, j = OldNumOfBdrElements; i<parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
parent_to_submesh_face_ids_[parentFaceIdx];
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (GetFaceInformation(submeshFaceIdx).IsBoundary())
|
||||
{ continue; }
|
||||
|
||||
boundary[j] = faces[submeshFaceIdx]->Duplicate(this);
|
||||
be_to_face[j] = submeshFaceIdx;
|
||||
boundary[j]->SetAttribute(parent.GetBdrAttribute(i));
|
||||
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Dim > 2)
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
GetElementToFaceTable();
|
||||
}
|
||||
@@ -308,6 +376,84 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
|
||||
Transfer(*pn, *n);
|
||||
}
|
||||
|
||||
if (Dim > 1)
|
||||
{
|
||||
if (!el_to_edge) { el_to_edge = new Table; }
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
if (Dim > 1 && from == SubMesh::From::Domain)
|
||||
{
|
||||
// Order 0 Raviart-Thomas space will have precisely 1 DoF per face.
|
||||
// We can use this DoF to communicate boundary attribute numbers.
|
||||
RT_FECollection fec_rt(0, Dim);
|
||||
ParFiniteElementSpace parent_fes_rt(const_cast<ParMesh*>(&parent),
|
||||
&fec_rt);
|
||||
|
||||
ParGridFunction parent_bdr_attr_gf(&parent_fes_rt);
|
||||
parent_bdr_attr_gf = 0.0;
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation doftrans;
|
||||
int dof, faceIdx;
|
||||
real_t sign, w;
|
||||
|
||||
// Copy boundary attribute numbers into local portion of a parallel
|
||||
// grid function
|
||||
parent_bdr_attr_gf.HostReadWrite(); // not modifying all entries
|
||||
for (int i=0; i<parent.GetNBE(); i++)
|
||||
{
|
||||
faceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const FaceInformation &faceInfo = parent.GetFaceInformation(faceIdx);
|
||||
parent_fes_rt.GetBdrElementDofs(i, vdofs, doftrans);
|
||||
dof = ParFiniteElementSpace::DecodeDof(vdofs[0], sign);
|
||||
|
||||
// Shared interior boundary elements are not duplicated across
|
||||
// processor boundaries but ParGridFunction::ParallelAverage will
|
||||
// assume both processors contribute to the averaged DoF value. So,
|
||||
// we multiply shared boundary values by 2 so that the average
|
||||
// produces the desired value.
|
||||
w = faceInfo.IsShared() ? 2.0 : 1.0;
|
||||
|
||||
// The DoF sign is needed to ensure that non-shared interior
|
||||
// boundary values sum properly rather than canceling.
|
||||
parent_bdr_attr_gf[dof] = sign * w * parent.GetBdrAttribute(i);
|
||||
}
|
||||
|
||||
Vector parent_bdr_attr(parent_fes_rt.GetTrueVSize());
|
||||
|
||||
// Compute the average of the attribute numbers
|
||||
parent_bdr_attr_gf.ParallelAverage(parent_bdr_attr);
|
||||
// Distribute boundary attributes to neighboring processors
|
||||
parent_bdr_attr_gf.Distribute(parent_bdr_attr);
|
||||
|
||||
ParFiniteElementSpace submesh_fes_rt(this,
|
||||
&fec_rt);
|
||||
|
||||
ParGridFunction submesh_bdr_attr_gf(&submesh_fes_rt);
|
||||
|
||||
// Transfer the averaged boundary attribute values to the submesh
|
||||
auto transfer_map = ParSubMesh::CreateTransferMap(parent_bdr_attr_gf,
|
||||
submesh_bdr_attr_gf);
|
||||
transfer_map.Transfer(parent_bdr_attr_gf, submesh_bdr_attr_gf);
|
||||
|
||||
// Extract the boundary attribute numbers from the local portion
|
||||
// of the ParGridFunction and set the corresponding boundary element
|
||||
// attributes.
|
||||
int attr;
|
||||
for (int i=0; i<NumOfBdrElements; i++)
|
||||
{
|
||||
submesh_fes_rt.GetBdrElementDofs(i, vdofs, doftrans);
|
||||
dof = ParFiniteElementSpace::DecodeDof(vdofs[0], sign);
|
||||
attr = (int)std::round(std::abs(submesh_bdr_attr_gf[dof]));
|
||||
if (attr != 0)
|
||||
{
|
||||
SetBdrAttribute(i, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAttributes();
|
||||
Finalize();
|
||||
}
|
||||
@@ -348,7 +494,6 @@ void ParSubMesh::FindSharedVerticesRanks(Array<int> &rhvtx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
svert_comm.Reduce(rhvtx, GroupCommunicator::Sum);
|
||||
svert_comm.Bcast<int>(rhvtx, 0);
|
||||
@@ -366,8 +511,8 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
rhe.SetSize(nsedges);
|
||||
rhe = 0;
|
||||
|
||||
// On each rank of the group, locally determine if the shared edge is in the
|
||||
// SubMesh.
|
||||
// On each rank of the group, locally determine if the shared edge is in
|
||||
// the SubMesh.
|
||||
for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
@@ -383,7 +528,8 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
|
||||
for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
|
||||
{
|
||||
int ple = parent_.GroupEdge(g, ge);
|
||||
int ple, o;
|
||||
parent_.GroupEdge(g, ge, ple, o);
|
||||
int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
|
||||
if (submesh_edge_id != -1)
|
||||
{
|
||||
@@ -392,7 +538,6 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
sedge_comm.Reduce(rhe, GroupCommunicator::Sum);
|
||||
sedge_comm.Bcast<int>(rhe, 0);
|
||||
@@ -400,48 +545,22 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
|
||||
void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
|
||||
{
|
||||
GroupCommunicator stria_comm(parent_.gtopo);
|
||||
parent_.GetSharedTriCommunicator(stria_comm);
|
||||
int nstria = stria_comm.GroupLDofTable().Size_of_connections();
|
||||
rht.SetSize(nstria);
|
||||
rht = 0;
|
||||
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
MFEM_ASSERT(parent_.gtopo.GetGroupSize(g) == 2
|
||||
|| parent_.GroupNTriangles(g) == 0,
|
||||
parent_.gtopo.GetGroupSize(g) << ' ' << parent_.GroupNTriangles(g));
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plt = parent_.GroupTriangle(g, gt);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plt];
|
||||
if (submesh_face_id != -1)
|
||||
{
|
||||
rht[st] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
stria_comm.Reduce(rht, GroupCommunicator::Sum);
|
||||
stria_comm.Bcast<int>(rht, 0);
|
||||
|
||||
GroupCommunicator squad_comm(parent_.gtopo);
|
||||
parent_.GetSharedQuadCommunicator(squad_comm);
|
||||
|
||||
int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
rhq.SetSize(nsquad);
|
||||
rhq = 0;
|
||||
|
||||
for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
MFEM_ASSERT(parent_.gtopo.GetGroupSize(g) == 2
|
||||
|| parent_.GroupNQuadrilaterals(g) == 0,
|
||||
parent_.gtopo.GetGroupSize(g) << ' ' << parent_.GroupNQuadrilaterals(g));
|
||||
for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plq = parent_.GroupQuadrilateral(g, gq);
|
||||
|
||||
int plq, o;
|
||||
parent_.GroupQuadrilateral(g, gq, plq, o);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plq];
|
||||
if (submesh_face_id != -1)
|
||||
{
|
||||
@@ -453,6 +572,34 @@ void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
squad_comm.Reduce(rhq, GroupCommunicator::Sum);
|
||||
squad_comm.Bcast<int>(rhq, 0);
|
||||
|
||||
GroupCommunicator stria_comm(parent_.gtopo);
|
||||
parent_.GetSharedTriCommunicator(stria_comm);
|
||||
|
||||
int nstria = stria_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
rht.SetSize(nstria);
|
||||
rht = 0;
|
||||
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
|
||||
int plt, o;
|
||||
parent_.GroupTriangle(g, gt, plt, o);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plt];
|
||||
if (submesh_face_id != -1)
|
||||
{
|
||||
rht[st] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
stria_comm.Reduce(rht, GroupCommunicator::Sum);
|
||||
stria_comm.Bcast<int>(rht, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -461,7 +608,6 @@ void ParSubMesh::AppendSharedVerticesGroups(ListOfIntegerSets &groups,
|
||||
{
|
||||
IntegerSet group;
|
||||
|
||||
// g = 0 corresponds to the singleton group of each rank alone.
|
||||
for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
@@ -533,7 +679,8 @@ void ParSubMesh::AppendSharedEdgesGroups(ListOfIntegerSets &groups,
|
||||
|
||||
for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
|
||||
{
|
||||
int ple = parent_.GroupEdge(g, ge);
|
||||
int ple, o;
|
||||
parent_.GroupEdge(g, ge, ple, o);
|
||||
int submesh_edge = parent_to_submesh_edge_ids_[ple];
|
||||
|
||||
// Reusing the `rhe` array as shared edge to group array.
|
||||
@@ -582,7 +729,8 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
MFEM_ASSERT(group_sz == 2, "internal error");
|
||||
|
||||
int plq = parent_.GroupQuadrilateral(g, gq);
|
||||
int plq, o;
|
||||
parent_.GroupQuadrilateral(g, gq, plq, o);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plq];
|
||||
|
||||
// Reusing the `rhq` array as shared face to group array.
|
||||
@@ -595,8 +743,8 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
{
|
||||
// shared face is present on this rank and others
|
||||
|
||||
// There can only be two ranks in this group sharing faces. Add all
|
||||
// ranks to a new communication group.
|
||||
// There can only be two ranks in this group sharing faces. Add
|
||||
// all ranks to a new communication group.
|
||||
Array<int> &ranks = quad_group;
|
||||
ranks.SetSize(0);
|
||||
ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
|
||||
@@ -622,7 +770,8 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
MFEM_ASSERT(group_sz == 2, "internal error");
|
||||
|
||||
int plt = parent_.GroupTriangle(g, gt);
|
||||
int plt, o;
|
||||
parent_.GroupTriangle(g, gt, plt, o);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plt];
|
||||
|
||||
// Reusing the `rht` array as shared face to group array.
|
||||
@@ -635,8 +784,8 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
{
|
||||
// shared face is present on this rank and others
|
||||
|
||||
// There can only be two ranks in this group sharing faces. Add all
|
||||
// ranks to a new communication group.
|
||||
// There can only be two ranks in this group sharing faces. Add
|
||||
// all ranks to a new communication group.
|
||||
Array<int> &ranks = tria_group;
|
||||
ranks.SetSize(0);
|
||||
ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
|
||||
@@ -653,46 +802,96 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
}
|
||||
}
|
||||
|
||||
void BuildGroup(Table &group, int ngroups, const Array<int>& rh, int &ns)
|
||||
{
|
||||
group.MakeI(ngroups);
|
||||
for (int i = 0; i < rh.Size(); i++)
|
||||
{
|
||||
if (rh[i] >= 0)
|
||||
{
|
||||
group.AddAColumnInRow(rh[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group.MakeJ();
|
||||
ns = 0;
|
||||
for (int i = 0; i < rh.Size(); i++)
|
||||
{
|
||||
if (rh[i] >= 0)
|
||||
{
|
||||
group.AddConnection(rh[i], ns++);
|
||||
}
|
||||
}
|
||||
group.ShiftUpI();
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildVertexGroup(int ngroups, const Array<int>& rhvtx,
|
||||
int& nsverts)
|
||||
{
|
||||
BuildGroup(group_svert, ngroups, rhvtx, nsverts);
|
||||
group_svert.MakeI(ngroups);
|
||||
for (int i = 0; i < rhvtx.Size(); i++)
|
||||
{
|
||||
if (rhvtx[i] >= 0)
|
||||
{
|
||||
group_svert.AddAColumnInRow(rhvtx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_svert.MakeJ();
|
||||
nsverts = 0;
|
||||
for (int i = 0; i < rhvtx.Size(); i++)
|
||||
{
|
||||
if (rhvtx[i] >= 0)
|
||||
{
|
||||
group_svert.AddConnection(rhvtx[i], nsverts++);
|
||||
}
|
||||
}
|
||||
group_svert.ShiftUpI();
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildEdgeGroup(int ngroups, const Array<int>& rhe,
|
||||
int& nsedges)
|
||||
{
|
||||
BuildGroup(group_sedge, ngroups, rhe, nsedges);
|
||||
group_sedge.MakeI(ngroups);
|
||||
for (int i = 0; i < rhe.Size(); i++)
|
||||
{
|
||||
if (rhe[i] >= 0)
|
||||
{
|
||||
group_sedge.AddAColumnInRow(rhe[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_sedge.MakeJ();
|
||||
nsedges = 0;
|
||||
for (int i = 0; i < rhe.Size(); i++)
|
||||
{
|
||||
if (rhe[i] >= 0)
|
||||
{
|
||||
group_sedge.AddConnection(rhe[i], nsedges++);
|
||||
}
|
||||
}
|
||||
group_sedge.ShiftUpI();
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildFaceGroup(int ngroups, const Array<int>& rht,
|
||||
int& nstrias, const Array<int>& rhq, int& nsquads)
|
||||
{
|
||||
BuildGroup(group_squad, ngroups, rhq, nsquads);
|
||||
BuildGroup(group_stria, ngroups, rht, nstrias);
|
||||
group_squad.MakeI(ngroups);
|
||||
for (int i = 0; i < rhq.Size(); i++)
|
||||
{
|
||||
if (rhq[i] >= 0)
|
||||
{
|
||||
group_squad.AddAColumnInRow(rhq[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_squad.MakeJ();
|
||||
nsquads = 0;
|
||||
for (int i = 0; i < rhq.Size(); i++)
|
||||
{
|
||||
if (rhq[i] >= 0)
|
||||
{
|
||||
group_squad.AddConnection(rhq[i], nsquads++);
|
||||
}
|
||||
}
|
||||
group_squad.ShiftUpI();
|
||||
|
||||
group_stria.MakeI(ngroups);
|
||||
for (int i = 0; i < rht.Size(); i++)
|
||||
{
|
||||
if (rht[i] >= 0)
|
||||
{
|
||||
group_stria.AddAColumnInRow(rht[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_stria.MakeJ();
|
||||
nstrias = 0;
|
||||
for (int i = 0; i < rht.Size(); i++)
|
||||
{
|
||||
if (rht[i] >= 0)
|
||||
{
|
||||
group_stria.AddConnection(rht[i], nstrias++);
|
||||
}
|
||||
}
|
||||
group_stria.ShiftUpI();
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildSharedVerticesMapping(const int nsverts,
|
||||
@@ -744,8 +943,8 @@ void ParSubMesh::BuildSharedEdgesMapping(const int sedges_ct,
|
||||
int v0 = parent_to_submesh_vertex_ids_[vert[(1-o)/2]];
|
||||
int v1 = parent_to_submesh_vertex_ids_[vert[(1+o)/2]];
|
||||
|
||||
// The orienation of the shared edge relative to the local edge will
|
||||
// be determined by whether v0 < v1 or v1 < v0
|
||||
// The orienation of the shared edge relative to the local edge
|
||||
// will be determined by whether v0 < v1 or v1 < v0
|
||||
shared_edges.Append(new Segment(v0, v1, 1));
|
||||
sedge_ledge.Append(submesh_edge_id);
|
||||
}
|
||||
@@ -761,8 +960,9 @@ void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
|
||||
shared_quads.Reserve(nsquads);
|
||||
sface_lface.Reserve(nstrias + nsquads);
|
||||
|
||||
// sface_lface should list the triangular shared faces first followed by the
|
||||
// quadrilateral shared faces.
|
||||
// sface_lface should list the triangular shared faces first
|
||||
// followed by the quadrilateral shared faces.
|
||||
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
@@ -828,7 +1028,7 @@ void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
|
||||
int v2 = vert[2];
|
||||
int v3 = vert[3];
|
||||
|
||||
// See Mesh::GetQuadOrientation for info on interpreting "o"
|
||||
// See Mesh::GetQuadOrientation for info on interpretting "o"
|
||||
switch (o)
|
||||
{
|
||||
case 1:
|
||||
@@ -857,254 +1057,10 @@ void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<int, int>
|
||||
ParSubMesh::FindGhostBoundaryElementAttributes() const
|
||||
{
|
||||
// Loop over shared faces in the parent mesh, find their attributes if they
|
||||
// exist, and map to local faces in the submesh.
|
||||
std::unordered_map<int,int> lface_boundary_attribute;
|
||||
const auto &face_to_be = parent_.GetFaceToBdrElMap();
|
||||
if (Dim == 3)
|
||||
{
|
||||
GroupCommunicator squad_comm(parent_.gtopo);
|
||||
parent_.GetSharedQuadCommunicator(squad_comm);
|
||||
int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
GroupCommunicator stria_comm(parent_.gtopo);
|
||||
parent_.GetSharedTriCommunicator(stria_comm);
|
||||
int nstria = stria_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
Array<int> stba(nstria), sqba(nsquad);
|
||||
Array<int> parent_ltface(nstria), parent_lqface(nsquad);
|
||||
stba = 0; sqba = 0;
|
||||
parent_ltface = -1; parent_lqface = -1;
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plt = parent_.GroupTriangle(g, gt);
|
||||
auto pbe = face_to_be[plt];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
stba[st] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_ltface[st] = plt;
|
||||
}
|
||||
}
|
||||
for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plq = parent_.GroupQuadrilateral(g, gq);
|
||||
auto pbe = face_to_be[plq];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
sqba[sq] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_lqface[sq] = plq;
|
||||
}
|
||||
}
|
||||
#ifdef MFEM_DEBUG
|
||||
auto pre_stba = stba;
|
||||
auto pre_sqba = sqba;
|
||||
#endif
|
||||
stria_comm.Reduce(stba, GroupCommunicator::Sum);
|
||||
stria_comm.Bcast<int>(stba, 0);
|
||||
squad_comm.Reduce(sqba, GroupCommunicator::Sum);
|
||||
squad_comm.Bcast<int>(sqba, 0);
|
||||
#ifdef MFEM_DEBUG
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(stba.Size());
|
||||
for (int i = 0; i < stba.Size(); i++)
|
||||
if (pre_stba[i] != 0 && pre_stba[i] != stba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared tri face: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(sqba.Size());
|
||||
for (int i = 0; i < sqba.Size(); i++)
|
||||
if (pre_sqba[i] != 0 && pre_sqba[i] != sqba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared quad face: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
#endif
|
||||
int nghost = 0;
|
||||
for (auto x : stba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
for (auto x : sqba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
lface_boundary_attribute.reserve(nghost);
|
||||
for (int i = 0; i < stba.Size(); i++)
|
||||
if (stba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_ltface[i] > -1, i);
|
||||
lface_boundary_attribute[parent_ltface[i]] = stba[i];
|
||||
}
|
||||
for (int i = 0; i < sqba.Size(); i++)
|
||||
if (sqba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_lqface[i] > -1, i);
|
||||
lface_boundary_attribute[parent_lqface[i]] = sqba[i];
|
||||
}
|
||||
}
|
||||
else if (Dim == 2)
|
||||
{
|
||||
GroupCommunicator sedge_comm(parent_.gtopo);
|
||||
parent_.GetSharedEdgeCommunicator(sedge_comm);
|
||||
int nsedge = sedge_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
Array<int> seba(nsedge), parent_ledge(nsedge);
|
||||
seba = 0; parent_ledge = -1;
|
||||
for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
|
||||
{
|
||||
// Group size of a shared edge is always 2
|
||||
int ple = parent_.GroupEdge(g, ge);
|
||||
auto pbe = face_to_be[ple];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
seba[se] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_ledge[se] = ple;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
auto pre_seba = seba;
|
||||
#endif
|
||||
sedge_comm.Reduce(seba, GroupCommunicator::Sum);
|
||||
sedge_comm.Bcast<int>(seba, 0);
|
||||
#ifdef MFEM_DEBUG
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(seba.Size());
|
||||
for (int i = 0; i < seba.Size(); i++)
|
||||
if (pre_seba[i] != 0 && pre_seba[i] != seba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared edge: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
#endif
|
||||
int nghost = 0;
|
||||
for (auto x : seba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
lface_boundary_attribute.reserve(nghost);
|
||||
for (int i = 0; i < seba.Size(); i++)
|
||||
if (seba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_ledge[i] > -1, i);
|
||||
lface_boundary_attribute[parent_ledge[i]] = seba[i];
|
||||
}
|
||||
}
|
||||
else if (Dim == 1)
|
||||
{
|
||||
GroupCommunicator svert_comm(parent_.gtopo);
|
||||
parent_.GetSharedVertexCommunicator(svert_comm);
|
||||
int nsvtx = svert_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
Array<int> svba(nsvtx), parent_lvtx(nsvtx);
|
||||
svba = 0; parent_lvtx = -1;
|
||||
for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
|
||||
{
|
||||
// Group size of a shared vertex is always 2
|
||||
int plv = parent_.GroupVertex(g, gv);
|
||||
auto pbe = face_to_be[plv];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
svba[sv] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_lvtx[sv] = plv;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
auto pre_svba = svba;
|
||||
#endif
|
||||
svert_comm.Reduce(svba, GroupCommunicator::Sum);
|
||||
svert_comm.Bcast<int>(svba, 0);
|
||||
#ifdef MFEM_DEBUG
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(svba.Size());
|
||||
for (int i = 0; i < svba.Size(); i++)
|
||||
if (pre_svba[i] != 0 && pre_svba[i] != svba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared vertex: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
#endif
|
||||
int nghost = 0;
|
||||
for (auto x : svba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
lface_boundary_attribute.reserve(nghost);
|
||||
for (int i = 0; i < svba.Size(); i++)
|
||||
if (svba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_lvtx[i] > -1, i);
|
||||
lface_boundary_attribute[parent_lvtx[i]] = svba[i];
|
||||
}
|
||||
}
|
||||
return lface_boundary_attribute;
|
||||
}
|
||||
|
||||
|
||||
void ParSubMesh::Transfer(const ParGridFunction &src, ParGridFunction &dst)
|
||||
{
|
||||
CreateTransferMap(src, dst).Transfer(src, dst);
|
||||
ParTransferMap map(src, dst);
|
||||
map.Transfer(src, dst);
|
||||
}
|
||||
|
||||
ParTransferMap ParSubMesh::CreateTransferMap(const ParGridFunction &src,
|
||||
|
||||
+20
-85
@@ -24,8 +24,6 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ParNCSubMesh;
|
||||
|
||||
/**
|
||||
* @brief Subdomain representation of a topological parent in another ParMesh.
|
||||
*
|
||||
@@ -52,13 +50,11 @@ class ParNCSubMesh;
|
||||
|
||||
class ParSubMesh : public ParMesh
|
||||
{
|
||||
friend class ParNCSubMesh;
|
||||
public:
|
||||
using From = SubMesh::From; ///< Convenience type-alias.
|
||||
ParSubMesh() = delete;
|
||||
|
||||
/**
|
||||
* @brief Create a domain ParSubMesh from its parent.
|
||||
* @brief Create a domain ParSubMesh from it's parent.
|
||||
*
|
||||
* The ParSubMesh object expects the parent ParMesh object to be valid for
|
||||
* the entire object lifetime. The @a domain_attributes have to mark exactly
|
||||
@@ -68,10 +64,10 @@ public:
|
||||
* @param[in] domain_attributes Domain attributes to extract
|
||||
*/
|
||||
static ParSubMesh CreateFromDomain(const ParMesh &parent,
|
||||
const Array<int> &domain_attributes);
|
||||
Array<int> &domain_attributes);
|
||||
|
||||
/**
|
||||
* @brief Create a surface ParSubMesh from its parent.
|
||||
* @brief Create a surface ParSubMesh from it's parent.
|
||||
*
|
||||
* The ParSubMesh object expects the parent ParMesh object to be valid for the
|
||||
* entire object lifetime. The @a boundary_attributes have to mark exactly one
|
||||
@@ -81,7 +77,7 @@ public:
|
||||
* @param[in] boundary_attributes Boundary attributes to extract
|
||||
*/
|
||||
static ParSubMesh CreateFromBoundary(const ParMesh &parent,
|
||||
const Array<int> &boundary_attributes);
|
||||
Array<int> &boundary_attributes);
|
||||
|
||||
/**
|
||||
* @brief Get the parent ParMesh object
|
||||
@@ -122,16 +118,6 @@ public:
|
||||
return parent_vertex_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the parent edge id map
|
||||
*
|
||||
* Submesh edge id (array index) to parent Mesh edge id.
|
||||
*/
|
||||
const Array<int>& GetParentEdgeIDMap() const
|
||||
{
|
||||
return parent_edge_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the parent face id map.
|
||||
*
|
||||
@@ -153,51 +139,13 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh element corresponding to a parent element. -1 ==
|
||||
* not present.
|
||||
* @param pe The parent element id.
|
||||
* @return int
|
||||
* @brief Get the ParSubMesh face id map.
|
||||
*
|
||||
* ParMesh face id (array index) to ParSubMesh face id.
|
||||
*/
|
||||
int GetSubMeshElementFromParent(int pe) const
|
||||
const Array<int>& GetParentToSubMeshFaceIDMap() const
|
||||
{
|
||||
return (pe == -1 || pe >= parent_to_submesh_element_ids_.Size())
|
||||
? -1 : parent_to_submesh_element_ids_[pe];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh vertex corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pv The parent vertex id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshVertexFromParent(int pv) const
|
||||
{
|
||||
return (pv == -1 || pv >= parent_to_submesh_vertex_ids_.Size())
|
||||
? -1 : parent_to_submesh_vertex_ids_[pv];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh edge corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pe The parent edge id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshEdgeFromParent(int pe) const
|
||||
{
|
||||
return (pe == -1 || pe >= parent_to_submesh_edge_ids_.Size())
|
||||
? pe : parent_to_submesh_edge_ids_[pe];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh face corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pf The parent face id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshFaceFromParent(int pf) const
|
||||
{
|
||||
return (pf == -1 || pf >= parent_to_submesh_face_ids_.Size())
|
||||
? pf : parent_to_submesh_face_ids_[pf];
|
||||
return parent_to_submesh_face_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,8 +183,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
const Array<int> &attributes);
|
||||
ParSubMesh(const ParMesh &parent, SubMesh::From from, Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Find shared vertices on the ParSubMesh.
|
||||
@@ -276,8 +223,8 @@ private:
|
||||
/**
|
||||
* @brief Find shared edges on the ParSubMesh.
|
||||
*
|
||||
* Uses the parent GroupCommunicator to determine shared edges. Collective.
|
||||
* Limited to groups containing less than 32 ranks.
|
||||
* Uses the parent GroupCommunicator to determine shared edges.
|
||||
* Collective. Limited to 32 ranks.
|
||||
*
|
||||
* See FindSharedVerticesRanks for the encoding for @a rhe.
|
||||
*
|
||||
@@ -285,7 +232,6 @@ private:
|
||||
*/
|
||||
void FindSharedEdgesRanks(Array<int> &rhe);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Find shared faces on the ParSubMesh.
|
||||
*
|
||||
@@ -329,10 +275,10 @@ private:
|
||||
* @param[in,out] groups
|
||||
* @param[in,out] rht Encoding of which rank contains which face triangle.
|
||||
* The output is reused s.t. the array index i (the face triangle id) is the
|
||||
* associated group. "Rank Has Triangle"
|
||||
* associated group.
|
||||
* @param[in,out] rhq Encoding of which rank contains which face
|
||||
* quadrilateral. The output is reused s.t. the array index i (the face
|
||||
* quadrilateral id) is the associated group. "Rank Has Quad"
|
||||
* quadrilateral id) is the associated group.
|
||||
*/
|
||||
void AppendSharedFacesGroups(ListOfIntegerSets &groups, Array<int>& rht,
|
||||
Array<int> &rhq);
|
||||
@@ -396,22 +342,15 @@ private:
|
||||
void BuildSharedFacesMapping(const int nstrias, const Array<int>& rht,
|
||||
const int nsquads, const Array<int>& rhq);
|
||||
|
||||
|
||||
std::unordered_map<int, int>
|
||||
FindGhostBoundaryElementAttributes() const;
|
||||
|
||||
/// The parent Mesh
|
||||
const ParMesh &parent_;
|
||||
|
||||
/// Optional nonconformal submesh. Managed via pncmesh pointer in base class.
|
||||
ParNCSubMesh *pncsubmesh_;
|
||||
|
||||
/// Indicator from which part of the parent ParMesh the ParSubMesh is going
|
||||
/// to be created.
|
||||
/// Indicator from which part of the parent ParMesh the ParSubMesh is going to
|
||||
/// be created.
|
||||
SubMesh::From from_;
|
||||
|
||||
/// Attributes on the parent ParMesh on which the ParSubMesh is created.
|
||||
/// Could either be domain or boundary attributes (determined by from_).
|
||||
/// Attributes on the parent ParMesh on which the ParSubMesh is created. Could
|
||||
/// either be domain or boundary attributes (determined by from_).
|
||||
Array<int> attributes_;
|
||||
|
||||
/// Mapping from ParSubMesh element ids (index of the array), to the parent
|
||||
@@ -430,14 +369,10 @@ private:
|
||||
/// ParMesh face ids.
|
||||
Array<int> parent_face_ids_;
|
||||
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation of
|
||||
/// the face relative to the parent face.
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation
|
||||
/// of the face relative to the parent face.
|
||||
Array<int> parent_face_ori_;
|
||||
|
||||
/// Mapping from parent ParMesh element ids (index of the array), to the
|
||||
/// ParSubMesh element ids. Inverse map of parent_element_ids_.
|
||||
Array<int> parent_to_submesh_element_ids_;
|
||||
|
||||
/// Mapping from parent ParMesh vertex ids (index of the array), to the
|
||||
/// ParSubMesh vertex ids. Inverse map of parent_vertex_ids_.
|
||||
Array<int> parent_to_submesh_vertex_ids_;
|
||||
|
||||
@@ -104,10 +104,10 @@ private:
|
||||
std::unique_ptr<const ParFiniteElementSpace> root_fes_;
|
||||
|
||||
/// Pointer to the supplemental FiniteElementCollection used with root_fes_.
|
||||
/// This is only used if this TransferMap represents a SubMesh to SubMesh
|
||||
/// transfer where the root requires a different type of collection than the
|
||||
/// SubMesh objects. For example, when the subpaces are L2 on boundaries of
|
||||
/// the parent mesh and the root space can be RT.
|
||||
/// This is only used if this TransferMap represents a SubMesh to
|
||||
/// SubMesh transfer where the root requires a different type of collection
|
||||
/// than the SubMesh objects. For example, when the subpaces are L2 on
|
||||
/// boundaries of the parent mesh and the root space can be RT.
|
||||
std::unique_ptr<const FiniteElementCollection> root_fec_;
|
||||
|
||||
const GroupCommunicator *root_gc_ = nullptr;
|
||||
|
||||
+35
-97
@@ -12,34 +12,36 @@
|
||||
#include "submesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include "../../fem/gridfunc.hpp"
|
||||
#include "../ncmesh.hpp"
|
||||
#include "ncsubmesh.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
SubMesh SubMesh::CreateFromDomain(const Mesh &parent,
|
||||
const Array<int> &domain_attributes)
|
||||
Array<int> domain_attributes)
|
||||
{
|
||||
return SubMesh(parent, From::Domain, domain_attributes);
|
||||
}
|
||||
|
||||
SubMesh SubMesh::CreateFromBoundary(const Mesh &parent,
|
||||
const Array<int> &boundary_attributes)
|
||||
Array<int> boundary_attributes)
|
||||
{
|
||||
return SubMesh(parent, From::Boundary, boundary_attributes);
|
||||
}
|
||||
|
||||
SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
const Array<int> &attributes) : parent_(&parent), from_(from),
|
||||
attributes_(attributes)
|
||||
Array<int> attributes) : parent_(parent), from_(from), attributes_(attributes)
|
||||
{
|
||||
if (parent.Nonconforming())
|
||||
{
|
||||
MFEM_ABORT("SubMesh does not support non-conforming meshes");
|
||||
}
|
||||
|
||||
if (from == From::Domain)
|
||||
{
|
||||
InitMesh(parent.Dimension(), parent.SpaceDimension(), 0, 0, 0);
|
||||
|
||||
std::tie(parent_vertex_ids_,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent, *this,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
|
||||
attributes_);
|
||||
}
|
||||
else if (from == From::Boundary)
|
||||
@@ -47,83 +49,39 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
InitMesh(parent.Dimension() - 1, parent.SpaceDimension(), 0, 0, 0);
|
||||
|
||||
std::tie(parent_vertex_ids_,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent, *this,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
|
||||
attributes_, true);
|
||||
}
|
||||
|
||||
parent_to_submesh_vertex_ids_.SetSize(parent.GetNV());
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
|
||||
}
|
||||
|
||||
parent_to_submesh_element_ids_.SetSize(from == From::Boundary ? parent.GetNBE()
|
||||
: parent.GetNE());
|
||||
parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < parent_element_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_element_ids_[parent_element_ids_[i]] = i;
|
||||
}
|
||||
|
||||
FinalizeTopology(false);
|
||||
|
||||
if (parent.Nonconforming())
|
||||
{
|
||||
ncmesh = new NCSubMesh(*this, *parent.ncmesh, from, attributes);
|
||||
ncsubmesh_ = dynamic_cast<NCSubMesh*>(ncmesh);
|
||||
InitFromNCMesh(*ncsubmesh_);
|
||||
ncsubmesh_->OnMeshUpdated(this);
|
||||
|
||||
// Update the submesh to parent vertex mapping, ncsubmesh_ reordered the
|
||||
// vertices so the map to parent is no longer valid.
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
// vertex -> node -> parent node -> parent vertex
|
||||
auto node = ncsubmesh_->vertex_nodeId[i];
|
||||
auto parent_node = ncsubmesh_->parent_node_ids_[node];
|
||||
auto parent_vertex = parent.ncmesh->GetNodeVertex(parent_node);
|
||||
parent_vertex_ids_[i] = parent_vertex;
|
||||
parent_to_submesh_vertex_ids_[parent_vertex] = i;
|
||||
}
|
||||
GenerateNCFaceInfo();
|
||||
SetAttributes();
|
||||
}
|
||||
|
||||
DSTable v2v(parent_->GetNV());
|
||||
parent_->GetVertexToVertexTable(v2v);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
Array<int> lv;
|
||||
GetEdgeVertices(i, lv);
|
||||
|
||||
// Find vertices/edge in parent mesh
|
||||
int parent_edge_id = v2v(parent_vertex_ids_[lv[0]],
|
||||
parent_vertex_ids_[lv[1]]);
|
||||
parent_edge_ids_.Append(parent_edge_id);
|
||||
}
|
||||
|
||||
parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
|
||||
parent_to_submesh_edge_ids_ = -1;
|
||||
for (int i = 0; i < parent_edge_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
|
||||
}
|
||||
FinalizeTopology(true);
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
parent_face_ids_ = SubMeshUtils::BuildFaceMap(parent, *this,
|
||||
parent_element_ids_);
|
||||
|
||||
parent_to_submesh_face_ids_.SetSize(parent.GetNFaces());
|
||||
parent_to_submesh_face_ids_ = -1;
|
||||
for (int i = 0; i < parent_face_ids_.Size(); i++)
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
parent_to_submesh_face_ids_[parent_face_ids_[i]] = i;
|
||||
int pbeid = parent_face_to_be[parent_face_ids_[GetBdrElementFaceIndex(i)]];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
int attr = parent.GetBdrElement(pbeid)->GetAttribute();
|
||||
GetBdrElement(i)->SetAttribute(attr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This case happens when a domain is extracted, but the root parent
|
||||
// mesh didn't have a boundary element on the surface that defined
|
||||
// it's boundary. It still creates a valid mesh, so we allow it.
|
||||
GetBdrElement(i)->SetAttribute(max_bdr_attr + 1);
|
||||
}
|
||||
}
|
||||
|
||||
parent_face_ori_.SetSize(NumOfFaces);
|
||||
|
||||
for (int i = 0; i < NumOfFaces; i++)
|
||||
{
|
||||
Array<int> sub_vert;
|
||||
@@ -137,6 +95,7 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
|
||||
Array<int> par_vert;
|
||||
parent.GetFaceVertices(parent_face_ids_[i], par_vert);
|
||||
|
||||
if (par_vert.Size() == 3)
|
||||
{
|
||||
parent_face_ori_[i] = GetTriOrientation(par_vert, sub_par_vert);
|
||||
@@ -153,14 +112,6 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
{
|
||||
parent_edge_ids_ = SubMeshUtils::BuildFaceMap(parent, *this,
|
||||
parent_element_ids_);
|
||||
|
||||
parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
|
||||
parent_to_submesh_edge_ids_ = -1;
|
||||
for (int i = 0; i < parent_edge_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
|
||||
}
|
||||
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
|
||||
@@ -174,10 +125,9 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
}
|
||||
else
|
||||
{
|
||||
// This case happens when a domain is extracted, but the root
|
||||
// parent mesh didn't have a boundary element on the surface that
|
||||
// defined it's boundary. It still creates a valid mesh, so we
|
||||
// allow it.
|
||||
// This case happens when a domain is extracted, but the root parent
|
||||
// mesh didn't have a boundary element on the surface that defined
|
||||
// it's boundary. It still creates a valid mesh, so we allow it.
|
||||
GetBdrElement(i)->SetAttribute(max_bdr_attr + 1);
|
||||
}
|
||||
}
|
||||
@@ -222,19 +172,6 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
}
|
||||
}
|
||||
|
||||
SubMeshUtils::AddBoundaryElements(*this);
|
||||
|
||||
if (Dim > 1)
|
||||
{
|
||||
delete el_to_edge;
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
if (Dim > 2)
|
||||
{
|
||||
GetElementToFaceTable();
|
||||
}
|
||||
|
||||
// If the parent Mesh has nodes and therefore is defined on a higher order
|
||||
// geometry, we define this SubMesh as a curved Mesh and transfer the
|
||||
// GridFunction from the parent Mesh to the SubMesh.
|
||||
@@ -258,7 +195,8 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
|
||||
void SubMesh::Transfer(const GridFunction &src, GridFunction &dst)
|
||||
{
|
||||
CreateTransferMap(src, dst).Transfer(src, dst);
|
||||
TransferMap map(src, dst);
|
||||
map.Transfer(src, dst);
|
||||
}
|
||||
|
||||
TransferMap SubMesh::CreateTransferMap(const GridFunction &src,
|
||||
|
||||
+22
-88
@@ -14,12 +14,11 @@
|
||||
|
||||
#include "../mesh.hpp"
|
||||
#include "transfermap.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class NCSubMesh;
|
||||
|
||||
/**
|
||||
* @brief Subdomain representation of a topological parent in another Mesh.
|
||||
*
|
||||
@@ -42,18 +41,17 @@ class NCSubMesh;
|
||||
*/
|
||||
class SubMesh : public Mesh
|
||||
{
|
||||
friend class NCSubMesh;
|
||||
public:
|
||||
/// Indicator from which part of the parent Mesh the SubMesh is created.
|
||||
enum class From
|
||||
enum From
|
||||
{
|
||||
Domain,
|
||||
Boundary
|
||||
};
|
||||
|
||||
static const int GENERATED_ATTRIBUTE = 900;
|
||||
|
||||
SubMesh() = delete;
|
||||
SubMesh(SubMesh &&) = default;
|
||||
SubMesh &operator=(SubMesh &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Create a domain SubMesh from its parent.
|
||||
@@ -66,7 +64,7 @@ public:
|
||||
* @param[in] domain_attributes Domain attributes to extract
|
||||
*/
|
||||
static SubMesh CreateFromDomain(const Mesh &parent,
|
||||
const Array<int> &domain_attributes);
|
||||
Array<int> domain_attributes);
|
||||
|
||||
/**
|
||||
* @brief Create a surface SubMesh from its parent.
|
||||
@@ -80,18 +78,22 @@ public:
|
||||
|
||||
*/
|
||||
static SubMesh CreateFromBoundary(const Mesh &parent,
|
||||
const Array<int> &boundary_attributes);
|
||||
Array<int> boundary_attributes);
|
||||
|
||||
///Get the parent Mesh object
|
||||
/**
|
||||
* @brief Get the parent Mesh object
|
||||
*
|
||||
*/
|
||||
const Mesh* GetParent() const
|
||||
{
|
||||
return parent_;
|
||||
return &parent_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the From indicator.
|
||||
*
|
||||
* Indicates whether the SubMesh has been created from a domain or surface.
|
||||
* Indicates whether the SubMesh has been created from a domain or
|
||||
* surface.
|
||||
*/
|
||||
From GetFrom() const
|
||||
{
|
||||
@@ -111,23 +113,13 @@ public:
|
||||
/**
|
||||
* @brief Get the face id map
|
||||
*
|
||||
* SubMesh face id (array index) to parent Mesh face id.
|
||||
* SubMesh element id (array index) to parent Mesh face id.
|
||||
*/
|
||||
const Array<int>& GetParentFaceIDMap() const
|
||||
{
|
||||
return parent_face_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the edge id map
|
||||
*
|
||||
* Submesh edge id (array index) to parent Mesh edge id.
|
||||
*/
|
||||
const Array<int>& GetParentEdgeIDMap() const
|
||||
{
|
||||
return parent_edge_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the relative face orientations
|
||||
*
|
||||
@@ -148,47 +140,6 @@ public:
|
||||
return parent_vertex_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh element corresponding to a parent element. -1 ==
|
||||
* not present.
|
||||
* @param pe The parent element id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshElementFromParent(int pe) const
|
||||
{
|
||||
return pe == -1 ? pe : parent_to_submesh_element_ids_[pe];
|
||||
}
|
||||
/**
|
||||
* @brief Get the submesh vertex corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pv The parent vertex id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshVertexFromParent(int pv) const
|
||||
{
|
||||
return pv == -1 ? pv : parent_to_submesh_vertex_ids_[pv];
|
||||
}
|
||||
/**
|
||||
* @brief Get the submesh edge corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pe The parent edge id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshEdgeFromParent(int pe) const
|
||||
{
|
||||
return pe == -1 ? pe : parent_to_submesh_edge_ids_[pe];
|
||||
}
|
||||
/**
|
||||
* @brief Get the submesh face corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pf The parent face id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshFaceFromParent(int pf) const
|
||||
{
|
||||
return pf == -1 ? pf : parent_to_submesh_face_ids_[pf];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer the dofs of a GridFunction.
|
||||
*
|
||||
@@ -205,8 +156,8 @@ public:
|
||||
/**
|
||||
* @brief Create a Transfer Map object.
|
||||
*
|
||||
* The @a src GridFunction can either be defined on a Mesh or a SubMesh and
|
||||
* is transferred appropriately.
|
||||
* The @a src GridFunction can either be defined on a Mesh or a
|
||||
* SubMesh and is transferred appropriately.
|
||||
*
|
||||
* @note Either @a src or @a dst has to be defined on a SubMesh.
|
||||
*/
|
||||
@@ -225,13 +176,10 @@ public:
|
||||
|
||||
private:
|
||||
/// Private constructor
|
||||
SubMesh(const Mesh &parent, From from, const Array<int> &attributes);
|
||||
SubMesh(const Mesh &parent, From from, Array<int> attributes);
|
||||
|
||||
/// The parent Mesh. Not owned.
|
||||
const Mesh *parent_;
|
||||
|
||||
/// Optional nonconformal submesh. Managed via ncmesh pointer in base class.
|
||||
NCSubMesh *ncsubmesh_;
|
||||
/// The parent Mesh
|
||||
const Mesh &parent_;
|
||||
|
||||
/// Indicator from which part of the parent ParMesh the ParSubMesh is going
|
||||
/// to be created.
|
||||
@@ -257,25 +205,11 @@ private:
|
||||
/// face ids.
|
||||
Array<int> parent_face_ids_;
|
||||
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation of
|
||||
/// the face relative to the parent face.
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation
|
||||
/// of the face relative to the parent face.
|
||||
Array<int> parent_face_ori_;
|
||||
|
||||
/// Mapping from parent Mesh vertex ids (index of the array), to the SubMesh
|
||||
/// vertex ids. Inverse map of parent_element_ids_.
|
||||
Array<int> parent_to_submesh_element_ids_;
|
||||
|
||||
/// Mapping from parent Mesh vertex ids (index of the array), to the SubMesh
|
||||
/// vertex ids. Inverse map of parent_vertex_ids_.
|
||||
Array<int> parent_to_submesh_vertex_ids_;
|
||||
|
||||
/// Mapping from parent Mesh edge ids (index of the array), to the SubMesh
|
||||
/// edge ids. Inverse map of parent_edge_ids_.
|
||||
Array<int> parent_to_submesh_edge_ids_;
|
||||
|
||||
/// Mapping from parent Mesh face ids (index of the array), to the SubMesh
|
||||
/// face ids. Inverse map of parent_face_ids_.
|
||||
Array<int> parent_to_submesh_face_ids_;
|
||||
Array<int> face_to_be;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+16
-644
@@ -10,12 +10,6 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "submesh_utils.hpp"
|
||||
#include "ncsubmesh.hpp"
|
||||
#include "submesh.hpp"
|
||||
#include "pncsubmesh.hpp"
|
||||
#include "psubmesh.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -37,8 +31,7 @@ int UniqueIndexGenerator::Get(int i, bool &new_index)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ElementT>
|
||||
bool ElementHasAttribute(const ElementT &el, const Array<int> &attributes)
|
||||
bool ElementHasAttribute(const Element &el, const Array<int> &attributes)
|
||||
{
|
||||
for (int a = 0; a < attributes.Size(); a++)
|
||||
{
|
||||
@@ -56,38 +49,41 @@ AddElementsToMesh(const Mesh& parent,
|
||||
const Array<int> &attributes,
|
||||
bool from_boundary)
|
||||
{
|
||||
UniqueIndexGenerator vertex_ids;
|
||||
Array<int> parent_vertex_ids, parent_element_ids;
|
||||
Array<int> vert, submesh_vert;
|
||||
|
||||
UniqueIndexGenerator vertex_ids;
|
||||
const int ne = from_boundary ? parent.GetNBE() : parent.GetNE();
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
const Element *pel = from_boundary ?
|
||||
parent.GetBdrElement(i) : parent.GetElement(i);
|
||||
if (!HasAttribute(*pel, attributes)) { continue; }
|
||||
pel->GetVertices(vert);
|
||||
submesh_vert.SetSize(vert.Size());
|
||||
for (int iv = 0; iv < vert.Size(); iv++)
|
||||
if (!ElementHasAttribute(*pel, attributes)) { continue; }
|
||||
|
||||
Array<int> v;
|
||||
pel->GetVertices(v);
|
||||
Array<int> submesh_v(v.Size());
|
||||
|
||||
for (int iv = 0; iv < v.Size(); iv++)
|
||||
{
|
||||
bool new_vertex;
|
||||
int mesh_vertex_id = vert[iv];
|
||||
int mesh_vertex_id = v[iv];
|
||||
int submesh_vertex_id = vertex_ids.Get(mesh_vertex_id, new_vertex);
|
||||
if (new_vertex)
|
||||
{
|
||||
mesh.AddVertex(parent.GetVertex(mesh_vertex_id));
|
||||
parent_vertex_ids.Append(mesh_vertex_id);
|
||||
}
|
||||
submesh_vert[iv] = submesh_vertex_id;
|
||||
submesh_v[iv] = submesh_vertex_id;
|
||||
}
|
||||
|
||||
Element *el = mesh.NewElement(from_boundary ?
|
||||
parent.GetBdrElementType(i) : parent.GetElementType(i));
|
||||
el->SetVertices(submesh_vert);
|
||||
el->SetVertices(submesh_v);
|
||||
el->SetAttribute(pel->GetAttribute());
|
||||
mesh.AddElement(el);
|
||||
parent_element_ids.Append(i);
|
||||
}
|
||||
return {parent_vertex_ids, parent_element_ids};
|
||||
return std::tuple<Array<int>, Array<int>>(parent_vertex_ids,
|
||||
parent_element_ids);
|
||||
}
|
||||
|
||||
void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
@@ -98,6 +94,7 @@ void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
{
|
||||
auto *m = subfes.GetMesh();
|
||||
vdof_to_vdof_map.SetSize(subfes.GetVSize());
|
||||
|
||||
const int vdim = parentfes.GetVDim();
|
||||
|
||||
IntegrationPointTransformation Tr;
|
||||
@@ -191,29 +188,6 @@ void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
(sub_sign * parent_sign > 0.0) ? parent_vdof : (-1-parent_vdof);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
auto tmp = vdof_to_vdof_map;
|
||||
tmp.Sort();
|
||||
tmp.Unique();
|
||||
|
||||
if (tmp.Size() != vdof_to_vdof_map.Size())
|
||||
{
|
||||
std::stringstream msg;
|
||||
for (int i = 0; i < vdof_to_vdof_map.Size(); i++)
|
||||
for (int j = i + 1; j < vdof_to_vdof_map.Size(); j++)
|
||||
{
|
||||
auto x = vdof_to_vdof_map[i];
|
||||
auto y = vdof_to_vdof_map[j];
|
||||
if (x == y)
|
||||
{
|
||||
msg << "i " << i << " (" << x << ") j " << j << " (" << y << ")\n";
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("vdof_to_vdof_map should be 1 to 1:\n" << msg.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Array<int> BuildFaceMap(const Mesh& pm, const Mesh& sm,
|
||||
@@ -253,607 +227,5 @@ Array<int> BuildFaceMap(const Mesh& pm, const Mesh& sm,
|
||||
return pfids;
|
||||
}
|
||||
|
||||
template <typename SubMeshT>
|
||||
void AddBoundaryElements(SubMeshT &mesh,
|
||||
const std::unordered_map<int,int> &lface_to_boundary_attribute)
|
||||
{
|
||||
mesh.Dimension();
|
||||
const int num_codim_1 = [&mesh]()
|
||||
{
|
||||
auto Dim = mesh.Dimension();
|
||||
if (Dim == 1) { return mesh.GetNV(); }
|
||||
else if (Dim == 2) { return mesh.GetNEdges(); }
|
||||
else if (Dim == 3) { return mesh.GetNFaces(); }
|
||||
else { MFEM_ABORT("Invalid dimension."); return -1; }
|
||||
}();
|
||||
|
||||
if (mesh.Dimension() == 3)
|
||||
{
|
||||
// In 3D we check for `bel_to_edge`. It shouldn't have been set
|
||||
// previously.
|
||||
mesh.DeleteBoundaryElementToEdge();
|
||||
}
|
||||
int NumOfBdrElements = 0;
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (mesh.GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
NumOfBdrElements++;
|
||||
}
|
||||
}
|
||||
|
||||
Array<Element *> boundary;
|
||||
Array<int> be_to_face;
|
||||
boundary.Reserve(NumOfBdrElements);
|
||||
be_to_face.Reserve(NumOfBdrElements);
|
||||
|
||||
const auto &parent = *mesh.GetParent();
|
||||
const auto &parent_face_ids = mesh.GetParentFaceIDMap();
|
||||
const auto &parent_edge_ids = mesh.GetParentEdgeIDMap();
|
||||
const auto &parent_vertex_ids = mesh.GetParentVertexIDMap();
|
||||
const auto &parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
const auto &face_to_be = mesh.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
auto pfid = [&](int i)
|
||||
{
|
||||
switch (mesh.Dimension())
|
||||
{
|
||||
case 3: return parent_face_ids[i];
|
||||
case 2: return parent_edge_ids[i];
|
||||
case 1: return parent_vertex_ids[i];
|
||||
}
|
||||
MFEM_ABORT("!");
|
||||
return -1;
|
||||
};
|
||||
if (mesh.GetFaceInformation(i).IsBoundary()
|
||||
&& (face_to_be.IsEmpty() || face_to_be[i] == -1))
|
||||
{
|
||||
auto * be = mesh.GetFace(i)->Duplicate(&mesh);
|
||||
|
||||
if (mesh.GetFrom() == SubMesh::From::Domain && mesh.Dimension() >= 2)
|
||||
{
|
||||
int pbeid = parent_face_to_be[pfid(i)];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
be->SetAttribute(parent.GetBdrAttribute(pbeid));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ghost_attr = lface_to_boundary_attribute.find(pfid(i));
|
||||
int battr = ghost_attr != lface_to_boundary_attribute.end() ?
|
||||
ghost_attr->second : max_bdr_attr + 1;
|
||||
be->SetAttribute(battr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ghost_attr = lface_to_boundary_attribute.find(pfid(i));
|
||||
int battr = ghost_attr != lface_to_boundary_attribute.end() ?
|
||||
ghost_attr->second : max_bdr_attr + 1;
|
||||
be->SetAttribute(battr);
|
||||
}
|
||||
be_to_face.Append(i);
|
||||
boundary.Append(be);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.GetFrom() == SubMesh::From::Domain && mesh.Dimension() >= 2)
|
||||
{
|
||||
// Search for and count interior boundary elements
|
||||
int InteriorBdrElems = 0;
|
||||
for (int i=0; i<parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
mesh.Dimension() == 3 ?
|
||||
mesh.GetSubMeshFaceFromParent(parentFaceIdx) :
|
||||
mesh.GetSubMeshEdgeFromParent(parentFaceIdx);
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (mesh.GetFaceInformation(submeshFaceIdx).IsBoundary()) { continue; }
|
||||
InteriorBdrElems++;
|
||||
}
|
||||
|
||||
if (InteriorBdrElems > 0)
|
||||
{
|
||||
NumOfBdrElements += InteriorBdrElems;
|
||||
boundary.Reserve(NumOfBdrElements);
|
||||
be_to_face.Reserve(NumOfBdrElements);
|
||||
|
||||
// Search for and transfer interior boundary elements
|
||||
for (int i = 0; i < parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
mesh.GetSubMeshFaceFromParent(parentFaceIdx);
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (mesh.GetFaceInformation(submeshFaceIdx).IsBoundary())
|
||||
{ continue; }
|
||||
|
||||
auto * be = mesh.GetFace(submeshFaceIdx)->Duplicate(&mesh);
|
||||
be->SetAttribute(parent.GetBdrAttribute(i));
|
||||
boundary.Append(be);
|
||||
be_to_face.Append(submeshFaceIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
mesh.AddBdrElements(boundary, be_to_face);
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
template void AddBoundaryElements(SubMesh &mesh,
|
||||
const std::unordered_map<int,int> &);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
template void AddBoundaryElements(ParSubMesh &mesh,
|
||||
const std::unordered_map<int,int> &);
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* @brief Helper class for storing and comparing arrays of face nodes.
|
||||
* @details The comparison operator uses the sorted nodes and a lexicographic
|
||||
* compare so that two different orientations of the same set of nodes will be
|
||||
* identical. The actual nodes are stored unsorted as the ordering is important
|
||||
* for constructing the leaf-root relations.
|
||||
*/
|
||||
struct FaceNodes
|
||||
{
|
||||
std::array<int, NCMesh::MaxFaceNodes> nodes;
|
||||
bool operator<(FaceNodes t2) const
|
||||
{
|
||||
std::array<int, NCMesh::MaxFaceNodes> t1 = nodes;
|
||||
std::sort(t1.begin(), t1.end());
|
||||
std::sort(t2.nodes.begin(), t2.nodes.end());
|
||||
return std::lexicographical_compare(t1.begin(), t1.end(),
|
||||
t2.nodes.begin(), t2.nodes.end());
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Establish the Geometry::Type from an array of nodes
|
||||
*
|
||||
* @param nodes
|
||||
* @return Geometry::Type
|
||||
*/
|
||||
Geometry::Type FaceGeomFromNodes(const std::array<int, NCMesh::MaxFaceNodes>
|
||||
&nodes)
|
||||
{
|
||||
if (nodes[3] == -1) { return Geometry::Type::TRIANGLE; }
|
||||
if (nodes[0] == nodes[1] && nodes[2] == nodes[3]) { return Geometry::Type::SEGMENT; }
|
||||
return Geometry::Type::SQUARE;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template<typename NCSubMeshT>
|
||||
void ConstructFaceTree(NCSubMeshT &submesh, const Array<int> &attributes)
|
||||
{
|
||||
// Convenience references to avoid `submesh.` repeatedly.
|
||||
auto &parent_node_ids = submesh.parent_node_ids_;
|
||||
auto &parent_element_ids = submesh.parent_element_ids_;
|
||||
auto &parent_to_submesh_node_ids = submesh.parent_to_submesh_node_ids_;
|
||||
auto &parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
const auto &parent = *submesh.GetParent();
|
||||
|
||||
// Collect parent vertex nodes to add in sequence. Map from parent nodes to
|
||||
// the new element in the ncsubmesh.
|
||||
UniqueIndexGenerator node_ids;
|
||||
std::map<FaceNodes, int> pnodes_new_elem;
|
||||
std::set<int> new_nodes;
|
||||
parent_to_submesh_element_ids.reserve(parent.GetNumFaces());
|
||||
parent_element_ids.Reserve(parent.GetNumFaces());
|
||||
// Base class cast then const cast because GetFaceList uses just in time
|
||||
// construction.
|
||||
const auto &face_list = const_cast<NCMesh&>(static_cast<const NCMesh&>
|
||||
(parent)).GetFaceList();
|
||||
// Double indexing loop because begin() and end() do not align with index 0
|
||||
// and size-1.
|
||||
for (int i = 0, ipe = 0; ipe < parent.GetNumFaces(); i++)
|
||||
{
|
||||
const auto &face = parent.GetFace(i);
|
||||
if (face.Unused()) { continue; }
|
||||
ipe++; // actual possible parent element.
|
||||
if (!HasAttribute(face, attributes)
|
||||
|| face_list.GetMeshIdType(face.index) == NCMesh::NCList::MeshIdType::MASTER
|
||||
) { continue; }
|
||||
|
||||
FaceNodes fn{submesh.parent_->FindFaceNodes(face)};
|
||||
if (pnodes_new_elem.find(fn) != pnodes_new_elem.end()) { continue; }
|
||||
|
||||
// TODO: Internal nc submesh can be constructed and solved on, but the
|
||||
// transfer to the parent mesh can be erroneous, this is likely due to not
|
||||
// treating the changing orientation of internal faces for ncmesh within
|
||||
// the ptransfermap.
|
||||
MFEM_ASSERT(face.elem[0] < 0 || face.elem[1] < 0,
|
||||
"Internal nonconforming boundaries are not reliably supported yet.");
|
||||
auto face_geom = FaceGeomFromNodes(fn.nodes);
|
||||
int new_elem_id = submesh.AddElement(face_geom, face.attribute);
|
||||
|
||||
// Rank needs to be established by presence (or lack of) in the submesh.
|
||||
submesh.elements[new_elem_id].rank = [&parent, &face]()
|
||||
{
|
||||
auto rank0 = face.elem[0] >= 0 ? parent.GetElement(face.elem[0]).rank : -1;
|
||||
auto rank1 = face.elem[1] >= 0 ? parent.GetElement(face.elem[1]).rank : -1;
|
||||
if (rank0 < 0) { return rank1; }
|
||||
if (rank1 < 0) { return rank0; }
|
||||
return rank0 < rank1 ? rank0 : rank1;
|
||||
}();
|
||||
pnodes_new_elem[fn] = new_elem_id;
|
||||
parent_element_ids.Append(i);
|
||||
parent_to_submesh_element_ids[i] = new_elem_id;
|
||||
|
||||
// Copy in the parent nodes. These will be relabeled once the tree is
|
||||
// built.
|
||||
std::copy(fn.nodes.begin(), fn.nodes.end(), submesh.elements[new_elem_id].node);
|
||||
for (auto x : fn.nodes)
|
||||
if (x != -1)
|
||||
{
|
||||
new_nodes.insert(x);
|
||||
}
|
||||
auto &gi = submesh.GI[face_geom];
|
||||
gi.InitGeom(face_geom);
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
new_nodes.insert(submesh.ParentNodes().FindId(fn.nodes[gi.edges[e][0]],
|
||||
fn.nodes[gi.edges[e][1]]));
|
||||
}
|
||||
|
||||
/*
|
||||
- Check not top level face
|
||||
- Check for parent of the newly entered element
|
||||
- if not present, add in
|
||||
- if present but different order and this path is non-ambiguous,
|
||||
reorder so consistent with child elements.
|
||||
- Set .parent in the newly entered element
|
||||
Break if top level face or joined existing branch (without reordering).
|
||||
|
||||
child element indices will be set afterwards because the orientation can change
|
||||
during traversal.
|
||||
*/
|
||||
bool root_path_is_ambiguous=false;
|
||||
bool fix_parent = false, tri_face = (face_geom == Geometry::TRIANGLE);
|
||||
while (true)
|
||||
{
|
||||
int child = submesh.parent_->ParentFaceNodes(fn.nodes);
|
||||
if (tri_face && child == 3)
|
||||
{
|
||||
// Traversing a central triangle face involves flipping the face orientation.
|
||||
// Do not use this pathway for reordering any parent face's nodes.
|
||||
root_path_is_ambiguous = true;
|
||||
}
|
||||
|
||||
if (child == -1) // A root face
|
||||
{
|
||||
submesh.elements[new_elem_id].parent = -1;
|
||||
break;
|
||||
}
|
||||
auto pelem = pnodes_new_elem.find(fn);
|
||||
bool new_parent = pelem == pnodes_new_elem.end();
|
||||
if (new_parent)
|
||||
{
|
||||
// Add in this parent
|
||||
int pelem_id = submesh.AddElement(FaceGeomFromNodes(fn.nodes), face.attribute);
|
||||
pelem = pnodes_new_elem.emplace(fn, pelem_id).first;
|
||||
auto parent_face_id = submesh.ParentFaces().FindId(fn.nodes[0], fn.nodes[1],
|
||||
fn.nodes[2],
|
||||
fn.nodes[3]);
|
||||
parent_element_ids.Append(parent_face_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are two scenarios where the parent nodes should be
|
||||
// rearranged:
|
||||
// 1. The found face is a slave, then the master might have been
|
||||
// added in reverse orientation
|
||||
// 2. The parent face was added from the central face of a triangle,
|
||||
// the orientation of the parent face is only fixed relative to
|
||||
// the outer child faces not the interior. If either of these
|
||||
// scenarios, and there's a mismatch, then reorder the parent and
|
||||
// all ancestors if necessary.
|
||||
if (!root_path_is_ambiguous &&
|
||||
!std::equal(fn.nodes.begin(), fn.nodes.end(), pelem->first.nodes.begin()))
|
||||
{
|
||||
fix_parent = true;
|
||||
auto pelem_id = pelem->second;
|
||||
MFEM_ASSERT(!submesh.elements[pelem_id].IsLeaf(), pelem_id);
|
||||
|
||||
// Re-key the map, the existing entry is inconsistent with the tree.
|
||||
pnodes_new_elem.erase(pelem->first);
|
||||
pelem = pnodes_new_elem.emplace(fn, pelem_id).first;
|
||||
}
|
||||
}
|
||||
// Ensure parent element is marked as non-leaf, and attach to the child.
|
||||
submesh.elements[pelem->second].ref_type = submesh.Dim == 2 ? Refinement::XY :
|
||||
Refinement::X;
|
||||
submesh.elements[new_elem_id].parent = pelem->second;
|
||||
|
||||
// If this was neither new nor a fixed parent, the higher levels of the
|
||||
// tree have been built, otherwise we recurse up the tree to add more parents, or
|
||||
// to potentially fix any ambiguously added FaceNodes.
|
||||
if (!new_parent && !fix_parent) { break; }
|
||||
|
||||
new_elem_id = pelem->second;
|
||||
}
|
||||
}
|
||||
parent_element_ids.ShrinkToFit();
|
||||
MFEM_ASSERT(parent_element_ids.Size() == submesh.elements.Size(),
|
||||
parent_element_ids.Size() << ' ' << submesh.elements.Size());
|
||||
|
||||
// All elements have been added, with their parents, and the nodal orientation of parents is
|
||||
// consistent with children, but the children indices have not been marked. Traverse the
|
||||
// tree from root to leaf to fill the child arrays.
|
||||
for (const auto & fn_elem : pnodes_new_elem)
|
||||
{
|
||||
auto fn = fn_elem.first;
|
||||
const auto &child_elem = submesh.elements[fn_elem.second];
|
||||
if (child_elem.parent == -1) { continue; }
|
||||
int child = submesh.parent_->ParentFaceNodes(fn.nodes);
|
||||
MFEM_ASSERT(pnodes_new_elem[fn] == child_elem.parent,
|
||||
pnodes_new_elem[fn] << ' ' << child_elem.parent);
|
||||
MFEM_ASSERT(submesh.elements[child_elem.parent].ref_type != char(0),
|
||||
int(submesh.elements[child_elem.parent].ref_type));
|
||||
submesh.elements[child_elem.parent].child[child] = fn_elem.second;
|
||||
}
|
||||
|
||||
/*
|
||||
All elements have been added into the tree but a) The nodes are all from
|
||||
the parent ncmesh b) The nodes do not know their parents c) The element
|
||||
ordering is wrong, root elements are not first d) The parent and child
|
||||
element numbers reflect the incorrect ordering
|
||||
|
||||
1. Add in nodes in the same order from the parent ncmesh
|
||||
2. Compute reordering of elements with parent elements first, that is
|
||||
stable across processors.
|
||||
*/
|
||||
// Build an inverse (and consecutive) map.
|
||||
Array<FaceNodes> new_elem_to_parent_face_nodes(pnodes_new_elem.size());
|
||||
for (const auto &kv : pnodes_new_elem)
|
||||
{
|
||||
new_elem_to_parent_face_nodes[kv.second] = kv.first;
|
||||
}
|
||||
pnodes_new_elem.clear(); // no longer needed
|
||||
|
||||
// Add new nodes preserving parent mesh ordering
|
||||
parent_node_ids.Reserve(static_cast<int>(new_nodes.size()));
|
||||
parent_to_submesh_node_ids.reserve(new_nodes.size());
|
||||
for (auto n : new_nodes)
|
||||
{
|
||||
bool new_node;
|
||||
auto new_node_id = node_ids.Get(n, new_node);
|
||||
MFEM_ASSERT(new_node, "!");
|
||||
submesh.nodes.Alloc(new_node_id, new_node_id, new_node_id);
|
||||
parent_node_ids.Append(n);
|
||||
parent_to_submesh_node_ids[n] = new_node_id;
|
||||
}
|
||||
parent_node_ids.ShrinkToFit();
|
||||
new_nodes.clear(); // not needed any more.
|
||||
|
||||
// Comparator for deciding order of elements. Building the ordering from the
|
||||
// parent ncmesh ensures the root ordering is common across ranks.
|
||||
auto comp_elements = [&](int l, int r)
|
||||
{
|
||||
const auto &elem_l = submesh.elements[l];
|
||||
const auto &elem_r = submesh.elements[r];
|
||||
if (elem_l.parent == elem_r.parent)
|
||||
{
|
||||
const auto &fnl = new_elem_to_parent_face_nodes[l].nodes;
|
||||
const auto &fnr = new_elem_to_parent_face_nodes[r].nodes;
|
||||
return std::lexicographical_compare(fnl.begin(), fnl.end(), fnr.begin(),
|
||||
fnr.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
return elem_l.parent < elem_r.parent;
|
||||
}
|
||||
};
|
||||
Array<int> indices(submesh.elements.Size());
|
||||
auto parental_sorted = [&]()
|
||||
{
|
||||
std::iota(indices.begin(), indices.end(), 0);
|
||||
return std::is_sorted(indices.begin(), indices.end(), comp_elements);
|
||||
};
|
||||
|
||||
Array<int> new_to_old(submesh.elements.Size()),
|
||||
old_to_new(submesh.elements.Size());
|
||||
while (!parental_sorted())
|
||||
{
|
||||
// Stably reorder elements in order of refinement, and by parental nodes
|
||||
// within a nuclear family.
|
||||
new_to_old.SetSize(submesh.elements.Size()),
|
||||
old_to_new.SetSize(submesh.elements.Size());
|
||||
std::iota(new_to_old.begin(), new_to_old.end(), 0);
|
||||
std::stable_sort(new_to_old.begin(), new_to_old.end(), comp_elements);
|
||||
// Build the inverse relation for converting the old elements to new
|
||||
for (int i = 0; i < submesh.elements.Size(); i++)
|
||||
{
|
||||
old_to_new[new_to_old[i]] = i;
|
||||
}
|
||||
|
||||
// Permute whilst reordering new_to_old. Avoids unnecessary copies.
|
||||
Permute(std::move(new_to_old), submesh.elements, parent_element_ids,
|
||||
new_elem_to_parent_face_nodes);
|
||||
parent_to_submesh_element_ids.clear();
|
||||
for (int i = 0; i < parent_element_ids.Size(); i++)
|
||||
{
|
||||
if (parent_element_ids[i] == -1) {continue;}
|
||||
parent_to_submesh_element_ids[parent_element_ids[i]] = i;
|
||||
}
|
||||
|
||||
// Apply the new ordering to child and parent elements
|
||||
for (auto &elem : submesh.elements)
|
||||
{
|
||||
if (!elem.IsLeaf())
|
||||
{
|
||||
// Parent rank is minimum of child ranks.
|
||||
elem.rank = std::numeric_limits<int>::max();
|
||||
for (int c = 0; c < NCMesh::MaxElemChildren && elem.child[c] >= 0; c++)
|
||||
{
|
||||
elem.child[c] = old_to_new[elem.child[c]];
|
||||
elem.rank = std::min(elem.rank, submesh.elements[elem.child[c]].rank);
|
||||
}
|
||||
}
|
||||
elem.parent = elem.parent == -1 ? -1 : old_to_new[elem.parent];
|
||||
}
|
||||
}
|
||||
|
||||
// Apply new node ordering to relations, and sign in on edges/vertices
|
||||
for (auto &elem : submesh.elements)
|
||||
{
|
||||
if (elem.IsLeaf())
|
||||
{
|
||||
bool new_id;
|
||||
auto &gi = submesh.GI[elem.Geom()];
|
||||
gi.InitGeom(elem.Geom());
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
const int pid = submesh.ParentNodes().FindId(
|
||||
elem.node[gi.edges[e][0]], elem.node[gi.edges[e][1]]);
|
||||
MFEM_ASSERT(pid >= 0,
|
||||
elem.node[gi.edges[e][0]] << ' ' << elem.node[gi.edges[e][1]]);
|
||||
auto submesh_node_id = node_ids.Get(pid, new_id);
|
||||
MFEM_ASSERT(!new_id, "!");
|
||||
submesh.nodes[submesh_node_id].edge_refc++;
|
||||
}
|
||||
for (int n = 0; n < gi.nv; n++)
|
||||
{
|
||||
MFEM_ASSERT(parent_to_submesh_node_ids.find(elem.node[n]) !=
|
||||
parent_to_submesh_node_ids.end(), "!");
|
||||
elem.node[n] = parent_to_submesh_node_ids[elem.node[n]];
|
||||
submesh.nodes[elem.node[n]].vert_refc++;
|
||||
}
|
||||
// Register faces
|
||||
for (int f = 0; f < gi.nf; f++)
|
||||
{
|
||||
auto *face = submesh.faces.Get(
|
||||
elem.node[gi.faces[f][0]],
|
||||
elem.node[gi.faces[f][1]],
|
||||
elem.node[gi.faces[f][2]],
|
||||
elem.node[gi.faces[f][3]]);
|
||||
face->attribute = -1;
|
||||
face->index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
template void ConstructFaceTree(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#ifdef MFEM_USE_MPI
|
||||
template void ConstructFaceTree(ParNCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#endif
|
||||
|
||||
template <typename NCSubMeshT>
|
||||
void ConstructVolumeTree(NCSubMeshT &submesh, const Array<int> &attributes)
|
||||
{
|
||||
// Convenience references to avoid `submesh.` repeatedly.
|
||||
auto &parent_node_ids = submesh.parent_node_ids_;
|
||||
auto &parent_element_ids = submesh.parent_element_ids_;
|
||||
auto &parent_to_submesh_node_ids = submesh.parent_to_submesh_node_ids_;
|
||||
auto &parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
const auto &parent = *submesh.GetParent();
|
||||
|
||||
UniqueIndexGenerator node_ids;
|
||||
parent_to_submesh_element_ids.reserve(parent.GetNumElements());
|
||||
std::set<int> new_nodes;
|
||||
for (int ipe = 0; ipe < parent.GetNumElements(); ipe++)
|
||||
{
|
||||
const auto& pe = parent.GetElement(ipe);
|
||||
if (!HasAttribute(pe, attributes)) { continue; }
|
||||
const int elem_id = submesh.AddElement(pe);
|
||||
auto &el = submesh.elements[elem_id];
|
||||
parent_element_ids.Append(ipe); // submesh -> parent
|
||||
parent_to_submesh_element_ids[ipe] = elem_id; // parent -> submesh
|
||||
if (!pe.IsLeaf()) { continue; }
|
||||
const auto gi = submesh.GI[pe.Geom()];
|
||||
for (int n = 0; n < gi.nv; n++)
|
||||
{
|
||||
new_nodes.insert(el.node[n]);
|
||||
}
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
new_nodes.insert(submesh.ParentNodes().FindId(el.node[gi.edges[e][0]],
|
||||
el.node[gi.edges[e][1]]));
|
||||
}
|
||||
}
|
||||
|
||||
parent_node_ids.Reserve(static_cast<int>(new_nodes.size()));
|
||||
parent_to_submesh_node_ids.reserve(new_nodes.size());
|
||||
for (const auto &n : new_nodes)
|
||||
{
|
||||
bool new_node;
|
||||
auto new_node_id = node_ids.Get(n, new_node);
|
||||
MFEM_ASSERT(new_node, "!");
|
||||
submesh.nodes.Alloc(new_node_id, new_node_id, new_node_id);
|
||||
parent_node_ids.Append(n);
|
||||
parent_to_submesh_node_ids[n] = new_node_id;
|
||||
}
|
||||
|
||||
// Loop over elements and reference edges and faces (creating any nodes on
|
||||
// first encounter).
|
||||
for (auto &el : submesh.elements)
|
||||
{
|
||||
if (el.IsLeaf())
|
||||
{
|
||||
const auto gi = submesh.GI[el.Geom()];
|
||||
bool new_id = false;
|
||||
|
||||
for (int n = 0; n < gi.nv; n++)
|
||||
{
|
||||
// Relabel nodes from parent to submesh.
|
||||
el.node[n] = node_ids.Get(el.node[n], new_id);
|
||||
MFEM_ASSERT(new_id == false, "Should not be new.");
|
||||
submesh.nodes[el.node[n]].vert_refc++;
|
||||
}
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
const int pid = submesh.ParentNodes().FindId(
|
||||
parent_node_ids[el.node[gi.edges[e][0]]],
|
||||
parent_node_ids[el.node[gi.edges[e][1]]]);
|
||||
MFEM_ASSERT(pid >= 0, "Edge not found");
|
||||
auto submesh_node_id = node_ids.Get(pid, new_id);
|
||||
MFEM_ASSERT(new_id == false, "Should not be new.");
|
||||
submesh.nodes[submesh_node_id].edge_refc++; // Register the edge
|
||||
}
|
||||
for (int f = 0; f < gi.nf; f++)
|
||||
{
|
||||
const int *fv = gi.faces[f];
|
||||
const int pid = submesh.ParentFaces().FindId(
|
||||
parent_node_ids[el.node[fv[0]]],
|
||||
parent_node_ids[el.node[fv[1]]],
|
||||
parent_node_ids[el.node[fv[2]]],
|
||||
el.node[fv[3]] >= 0 ? parent_node_ids[el.node[fv[3]]]: - 1);
|
||||
MFEM_ASSERT(pid >= 0, "Face not found");
|
||||
const int id = submesh.faces.GetId(
|
||||
el.node[fv[0]], el.node[fv[1]], el.node[fv[2]], el.node[fv[3]]);
|
||||
submesh.faces[id].attribute = submesh.ParentFaces()[pid].attribute;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All elements have been collected, remap the child ids.
|
||||
for (int i = 0; i < NCMesh::MaxElemChildren && el.child[i] >= 0; i++)
|
||||
{
|
||||
el.child[i] = parent_to_submesh_element_ids[el.child[i]];
|
||||
}
|
||||
}
|
||||
el.parent = el.parent < 0 ? el.parent
|
||||
: parent_to_submesh_element_ids.at(el.parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
template void ConstructVolumeTree(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#ifdef MFEM_USE_MPI
|
||||
template void ConstructVolumeTree(ParNCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#endif
|
||||
} // namespace SubMeshUtils
|
||||
} // namespace mfem
|
||||
|
||||
+12
-154
@@ -19,9 +19,6 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
class NCSubMesh;
|
||||
class ParNCSubMesh;
|
||||
|
||||
namespace SubMeshUtils
|
||||
{
|
||||
|
||||
@@ -43,6 +40,15 @@ struct UniqueIndexGenerator
|
||||
int Get(int i, bool &new_index);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Given an element @a el and a list of @a attributes, determine if that
|
||||
* element is in at least one attribute of @a attributes.
|
||||
*
|
||||
* @param el The element
|
||||
* @param attributes The attributes
|
||||
*/
|
||||
bool ElementHasAttribute(const Element &el, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Given a Mesh @a parent and another Mesh @a mesh using the list of
|
||||
* attributes in @a attributes, this function adds matching elements with those
|
||||
@@ -105,10 +111,10 @@ void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
* @tparam T The type of the input object which has to fulfill the
|
||||
* SubMesh::GetParent() interface.
|
||||
*/
|
||||
template <class T>
|
||||
auto GetRootParent(const T &m) -> decltype(std::declval<T>().GetParent())
|
||||
template <class T, class RT = decltype(std::declval<T>().GetParent())>
|
||||
RT GetRootParent(const T &m)
|
||||
{
|
||||
auto parent = m.GetParent();
|
||||
RT parent = m.GetParent();
|
||||
while (true)
|
||||
{
|
||||
const T* next = dynamic_cast<const T*>(parent);
|
||||
@@ -117,154 +123,6 @@ auto GetRootParent(const T &m) -> decltype(std::declval<T>().GetParent())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add boundary elements to the SubMesh.
|
||||
* @details An attempt to call this function for anything other than SubMesh or
|
||||
* ParSubMesh will result in a linker error as the template is only explicitly
|
||||
* instantiated for those types.
|
||||
* @param mesh The SubMesh to add boundary elements to.
|
||||
* @param lface_to_boundary_attribute Map from local faces in the submesh to
|
||||
* boundary attributes. Only necessary for interior boundary attributes of
|
||||
* volume submeshes, where the face owning the attribute might be on a
|
||||
* neighboring rank.
|
||||
* @tparam SubMeshT The SubMesh type, options SubMesh and ParSubMesh.
|
||||
*/
|
||||
template <typename SubMeshT>
|
||||
void AddBoundaryElements(SubMeshT &mesh,
|
||||
const std::unordered_map<int,int> &lface_to_boundary_attribute = {});
|
||||
|
||||
/**
|
||||
* @brief Construct a nonconformal mesh (serial or parallel) for a surface
|
||||
* submesh, from an existing nonconformal volume mesh (serial or parallel).
|
||||
* @details This function is only instantiated for NCSubMesh and ParNCSubMesh
|
||||
* Attempting to use it with other classes will result in a linker error.
|
||||
* @tparam NCSubMeshT The NCSubMesh type
|
||||
* @param[out] submesh The surface submesh to be filled.
|
||||
* @param attributes The set of attributes defining the submesh.
|
||||
*/
|
||||
template<typename NCSubMeshT>
|
||||
void ConstructFaceTree(NCSubMeshT &submesh, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Construct a nonconformal mesh (serial or parallel) for a volume
|
||||
* submesh, from an existing nonconformal volume mesh (serial or parallel).
|
||||
* @details This function is only instantiated for NCSubMesh and ParNCSubMesh
|
||||
* Attempting to use it with other classes will result in a linker error.
|
||||
* @tparam NCSubMeshT The NCSubMesh type
|
||||
* @param[out] submesh The volume submesh to be filled from parent.
|
||||
* @param attributes The set of attributes defining the submesh.
|
||||
*/
|
||||
template <typename NCSubMeshT>
|
||||
void ConstructVolumeTree(NCSubMeshT &submesh, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Helper for checking if an object's attributes match a list
|
||||
*
|
||||
* @tparam T Object Type
|
||||
* @param el Instance of T, requires method `GetAttribute()`
|
||||
* @param attributes Set of attributes to match against
|
||||
* @return true The attribute of el is contained within attributes
|
||||
* @return false
|
||||
*/
|
||||
template <typename T>
|
||||
bool HasAttribute(const T &el, const Array<int> &attributes)
|
||||
{
|
||||
for (int a = 0; a < attributes.Size(); a++)
|
||||
{
|
||||
if (el.GetAttribute() == attributes[a])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwarding dispatch to HasAttribute for backwards compatability
|
||||
*
|
||||
* @param el Instance of T, requires method `GetAttribute()`
|
||||
* @param attributes Set of attributes to match against
|
||||
* @return true The attribute of el is contained within attributes
|
||||
* @return false
|
||||
*/
|
||||
MFEM_DEPRECATED inline bool ElementHasAttribute(const Element &el,
|
||||
const Array<int> &attributes)
|
||||
{
|
||||
return HasAttribute(el,attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply permutation to a container type
|
||||
*
|
||||
* @tparam T1 Container type 1
|
||||
* @tparam T2 Container type 2
|
||||
* @tparam T3 Container type 3
|
||||
* @param indices Set of indices that define the permutation
|
||||
* @param t1 First collection to be permuted
|
||||
* @param t2 Second collection to be permuted
|
||||
* @param t3 Third collection to be permuted
|
||||
*/
|
||||
template <typename T1, typename T2, typename T3>
|
||||
void Permute(const Array<int>& indices, T1& t1, T2& t2, T3& t3)
|
||||
{
|
||||
Permute(Array<int>(indices), t1, t2, t3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply permutation to a container type
|
||||
* @details Sorts the indices variable in the process, thereby destroying the
|
||||
* permutation.
|
||||
*
|
||||
* @tparam T1 Container type 1
|
||||
* @tparam T2 Container type 2
|
||||
* @tparam T3 Container type 3
|
||||
* @param indices Set of indices that define the permutation
|
||||
* @param t1 First collection to be permuted
|
||||
* @param t2 Second collection to be permuted
|
||||
* @param t3 Third collection to be permuted
|
||||
*/
|
||||
template <typename T1, typename T2, typename T3>
|
||||
void Permute(Array<int>&& indices, T1& t1, T2& t2, T3& t3)
|
||||
{
|
||||
/*
|
||||
TODO: In c++17 can replace this with a parameter pack expansion technique to
|
||||
operate on arbitrary collections of reference accessible containers of
|
||||
arbitrary type.
|
||||
template <typename ...T> void Permute(Array<int>&&indices, T&... t)
|
||||
{
|
||||
for (int i = 0; i < indices.Size(); i++)
|
||||
{
|
||||
auto current = i;
|
||||
while (i != indices[current])
|
||||
{
|
||||
auto next = indices[current];
|
||||
// Lambda allows iteration over expansion in c++17
|
||||
// https://stackoverflow.com/a/60136761
|
||||
([&]{std::swap(t[current], t[next]);} (), ...);
|
||||
current = next;
|
||||
}
|
||||
indices[current] = current;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (int i = 0; i < indices.Size(); i++)
|
||||
{
|
||||
auto current = i;
|
||||
while (i != indices[current])
|
||||
{
|
||||
auto next = indices[current];
|
||||
std::swap(t1[current], t1[next]);
|
||||
std::swap(t2[current], t2[next]);
|
||||
std::swap(t3[current], t3[next]);
|
||||
indices[current] = current;
|
||||
current = next;
|
||||
}
|
||||
indices[current] = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace SubMeshUtils
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -323,10 +323,11 @@ int main(int argc, char *argv[])
|
||||
// Perform time-integration for the problem (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
bool done = false;
|
||||
for ( ; !done; )
|
||||
for (int ti = 0; !done; )
|
||||
{
|
||||
real_t dt_real = max(dt, t_final - t);
|
||||
cvodes->Step(*U, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
|
||||
@@ -221,10 +221,11 @@ int main(int argc, char *argv[])
|
||||
// Perform time-integration (looping over the time iterations, ti, with a
|
||||
// time-step dt).
|
||||
bool done = false;
|
||||
while (!done)
|
||||
for (int ti = 0; !done; )
|
||||
{
|
||||
real_t dt_real = max(dt, t_final - t);
|
||||
cvodes->Step(u, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
|
||||
@@ -0,0 +1,562 @@
|
||||
// contact-visualization test
|
||||
|
||||
// srun -n 8 ./contact-vis -testno 6 -paraview -sr 0 -pr 0 -tr 1.0 -sn 1.0
|
||||
// srun -n 8 ./contact-vis -testno 6 -paraview -sr 1 -pr 0 -tr 16.0 -sn 1.0
|
||||
// srun -n 8 ./contact-vis -testno 6 -paraview -sr 2 -pr 0 -tr 4.0 -sn 1.0
|
||||
// srun -n 8 ./contact-vis -testno 6 -paraview -sr 3 -pr 0 -tr 4.0 -sn 1.0
|
||||
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "axom/slic.hpp"
|
||||
|
||||
#include "tribol/interface/tribol.hpp"
|
||||
#include "tribol/interface/mfem_tribol.hpp"
|
||||
#include "tribol/mesh/CouplingScheme.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
void GetEssentialTdofs(const ParFiniteElementSpace & fes, const Array<int> & ess_bdr_attr,
|
||||
const Array<int> & ess_bdr_attr_comp, Array<int> & ess_tdof_list);
|
||||
HypreParMatrix * SetupTribol(ParMesh * pmesh, ParGridFunction * coords,
|
||||
const Array<int> & ess_tdofs, const std::set<int> & mortar_attrs,
|
||||
const std::set<int> & non_mortar_attrs,
|
||||
Vector &gap, double ratio, int tribol_nprocs);
|
||||
|
||||
HypreParMatrix * GetContactProlongation(ParFiniteElementSpace * fes,
|
||||
HypreParMatrix *J,
|
||||
Array<int> ess_tdof_list);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mpi::Init();
|
||||
int myid = Mpi::WorldRank();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
Hypre::Init();
|
||||
|
||||
int sref = 1;
|
||||
int pref = 0;
|
||||
bool visualization = true;
|
||||
bool paraview = false;
|
||||
bool visit = false;
|
||||
int testNo = -1; // 0-6
|
||||
double tribol_ratio = 8.0;
|
||||
double separation = 0.1;
|
||||
bool disable_essbdr = false;
|
||||
double scale_nodes = 1.0;
|
||||
int tribol_nprocs = num_procs;
|
||||
|
||||
// 1. Parse command-line options.
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&testNo, "-testno", "--test-number",
|
||||
"Choice of test problem:"
|
||||
"-1: default (original 2 block problem)"
|
||||
"0: not implemented yet"
|
||||
"1: not implemented yet"
|
||||
"2: not implemented yet"
|
||||
"3: not implemented yet"
|
||||
"4: two block problem - diablo"
|
||||
"41: two block problem - twisted"
|
||||
"5: ironing problem"
|
||||
"51: ironing problem extended"
|
||||
"6: nested spheres problem");
|
||||
args.AddOption(&sref, "-sr", "--serial-refinements",
|
||||
"Number of uniform refinements.");
|
||||
args.AddOption(&pref, "-pr", "--parallel-refinements",
|
||||
"Number of uniform refinements.");
|
||||
args.AddOption(&separation, "-s", "--mesh-separation",
|
||||
"Mesh separation distance.");
|
||||
args.AddOption(&tribol_ratio, "-tr", "--tribol-proximity-parameter",
|
||||
"Tribol-proximity-parameter.");
|
||||
args.AddOption(&scale_nodes, "-sn", "--scale-nodes",
|
||||
"Scale the nodes of the mesh.");
|
||||
args.AddOption(&disable_essbdr, "-noessbdr", "--no-ess-bdr", "-ess-bdr",
|
||||
"--ess-bdr",
|
||||
"Enable or disable essential boundary.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview", "-no-paraview",
|
||||
"--no-paraview",
|
||||
"Enable or disable ParaView visualization.");
|
||||
args.AddOption(&visit, "-visit", "--visit", "-no-visit",
|
||||
"--no-visit",
|
||||
"Enable or disable VisIT visualization.");
|
||||
args.AddOption(&tribol_nprocs, "-tn", "--tribol-nprocs",
|
||||
"Number of ranks used in tribol redecomposition" );
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out << "Visualizing test problem number: " << testNo << endl;
|
||||
}
|
||||
|
||||
const char *mesh_file = nullptr;
|
||||
|
||||
switch (testNo)
|
||||
{
|
||||
case 4:
|
||||
mesh_file = "meshes/Test4.mesh";
|
||||
break;
|
||||
case 40:
|
||||
mesh_file = "meshes/Test40.mesh";
|
||||
break;
|
||||
case 5:
|
||||
mesh_file = "meshes/Test5.mesh";
|
||||
break;
|
||||
case 51:
|
||||
mesh_file = "meshes/Test51.mesh";
|
||||
break;
|
||||
case 6:
|
||||
mesh_file = "meshes/Test6mod2.mesh";
|
||||
break;
|
||||
case -1:
|
||||
mesh_file = "meshes/two-block2.mesh";
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Should be unreachable");
|
||||
break;
|
||||
}
|
||||
|
||||
Mesh mesh(mesh_file,1);
|
||||
|
||||
for (int i = 0; i<sref; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// move nodes of mesh elements with attr = 2
|
||||
// mesh.EnsureNodes();
|
||||
// GridFunction * nodes = mesh.GetNodes();
|
||||
// Array<int> nodes_marker(nodes->Size());
|
||||
// nodes_marker = 0;
|
||||
// const FiniteElementSpace * meshfes = mesh.GetNodalFESpace();
|
||||
// Array<int> vdofs;
|
||||
// for (int i=0; i<mesh.GetNE(); i++)
|
||||
// {
|
||||
// int attr = mesh.GetAttribute(i);
|
||||
// if (attr == 2)
|
||||
// {
|
||||
// // get element size
|
||||
// double h = mesh.GetElementSize(i,0);
|
||||
// mfem::out << "attr 2 h = " << h << endl;
|
||||
// meshfes->GetElementVDofs(i,vdofs);
|
||||
// for (int j = 0; j<vdofs.Size()/3; j++)
|
||||
// {
|
||||
|
||||
// int xdof = vdofs[j];
|
||||
// if (!nodes_marker[xdof])
|
||||
// {
|
||||
// (*nodes)[xdof] += separation;
|
||||
// nodes_marker[xdof] = 1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// double h = mesh.GetElementSize(i,0);
|
||||
// mfem::out << "attr 1 h = " << h << endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD,mesh);
|
||||
mesh.Clear();
|
||||
for (int i = 0; i<pref; i++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
}
|
||||
|
||||
int dim = pmesh.Dimension();
|
||||
|
||||
std::set<int> mortar_attr;
|
||||
std::set<int> nonmortar_attr;
|
||||
Array<int> ess_bdr_attr;
|
||||
Array<int> ess_bdr_attr_comp;
|
||||
|
||||
// count faces of 9 and 8
|
||||
|
||||
if (testNo == 6)
|
||||
{
|
||||
mortar_attr.insert(6);
|
||||
mortar_attr.insert(9);
|
||||
nonmortar_attr.insert(7);
|
||||
nonmortar_attr.insert(8);
|
||||
|
||||
// mortar_attr.insert(8);
|
||||
// nonmortar_attr.insert(9);
|
||||
|
||||
|
||||
ess_bdr_attr.Append(1); ess_bdr_attr_comp.Append(1);
|
||||
ess_bdr_attr.Append(2); ess_bdr_attr_comp.Append(2);
|
||||
ess_bdr_attr.Append(4); ess_bdr_attr_comp.Append(0);
|
||||
ess_bdr_attr.Append(5); ess_bdr_attr_comp.Append(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mortar_attr.insert(3);
|
||||
nonmortar_attr.insert(4);
|
||||
ess_bdr_attr.Append(2); ess_bdr_attr_comp.Append(-1);
|
||||
ess_bdr_attr.Append(6); ess_bdr_attr_comp.Append(-1);
|
||||
}
|
||||
|
||||
H1_FECollection fec(1,dim);
|
||||
ParFiniteElementSpace fes(&pmesh,&fec,dim,Ordering::byVDIM);
|
||||
pmesh.SetNodalFESpace(&fes);
|
||||
|
||||
GridFunction * pnodes = pmesh.GetNodes();
|
||||
for (int i = 0; i < pnodes->Size(); i++)
|
||||
{
|
||||
(*pnodes)(i) *= scale_nodes;
|
||||
}
|
||||
|
||||
int gndofs = fes.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "--------------------------------------" << endl;
|
||||
mfem::out << "Global number of dofs = " << gndofs << endl;
|
||||
mfem::out << "--------------------------------------" << endl;
|
||||
}
|
||||
ParGridFunction contact_gf(&fes); contact_gf = 0.0;
|
||||
Vector contact_tdofs(fes.GetTrueVSize()); contact_tdofs = 0.0;
|
||||
ParaViewDataCollection * paraview_dc = nullptr;
|
||||
VisItDataCollection * visit_dc = nullptr;
|
||||
|
||||
// Get the essential true dofs
|
||||
Array<int> ess_tdof_list;
|
||||
if (!disable_essbdr)
|
||||
{
|
||||
GetEssentialTdofs(fes, ess_bdr_attr, ess_bdr_attr_comp, ess_tdof_list);
|
||||
}
|
||||
// Set up the Tribol contact problem
|
||||
ParGridFunction ref_coords(&fes);
|
||||
pmesh.GetNodes(ref_coords);
|
||||
Vector gap;
|
||||
HypreParMatrix * J = SetupTribol(&pmesh, &ref_coords, ess_tdof_list, mortar_attr, nonmortar_attr, gap, tribol_ratio, tribol_nprocs);
|
||||
|
||||
// Get the contact prolongation operator
|
||||
HypreParMatrix * Pc = GetContactProlongation(&fes, J, ess_tdof_list);
|
||||
|
||||
if (paraview)
|
||||
{
|
||||
std::ostringstream paraview_file_name;
|
||||
paraview_file_name << "ContactTribolVis_TestNo_" << testNo
|
||||
<< "_par_ref_" << pref
|
||||
<< "_ser_ref_" << sref
|
||||
<< "_tribol-scale_" << tribol_ratio
|
||||
<< "_mesh_scale_" << scale_nodes;
|
||||
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
|
||||
paraview_dc->SetPrefixPath("ParaView");
|
||||
paraview_dc->SetLevelsOfDetail(1);
|
||||
paraview_dc->SetDataFormat(VTKFormat::BINARY32);
|
||||
paraview_dc->SetHighOrderOutput(true);
|
||||
paraview_dc->RegisterField("u_c", &contact_gf);
|
||||
paraview_dc->SetCycle(0);
|
||||
paraview_dc->SetTime(0.0);
|
||||
paraview_dc->Save();
|
||||
}
|
||||
if (visit)
|
||||
{
|
||||
std::ostringstream visit_file_name;
|
||||
visit_file_name << "ContactTribolVis_TestNo_" << testNo
|
||||
<< "_par_ref_" << pref
|
||||
<< "_ser_ref_" << sref
|
||||
<< "_tribol-scale_" << tribol_ratio
|
||||
<< "_mesh_scale_" << scale_nodes;
|
||||
visit_dc = new VisItDataCollection(visit_file_name.str(), &pmesh);
|
||||
visit_dc->SetPrefixPath("VisIT");
|
||||
visit_dc->RegisterField("u_c", &contact_gf);
|
||||
visit_dc->SetCycle(0);
|
||||
visit_dc->SetTime(0.0);
|
||||
visit_dc->Save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sol_sock.open(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
}
|
||||
|
||||
int gncols = Pc->GetGlobalNumCols();
|
||||
int gnrows = Pc->GetGlobalNumRows();
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out << "--------------------------------------" << endl;
|
||||
mfem::out << "Global number of contact dofs = " << gncols << endl;
|
||||
mfem::out << "Global number of contact rows = " << gnrows << endl;
|
||||
mfem::out << "--------------------------------------" << endl;
|
||||
}
|
||||
|
||||
contact_tdofs = 0.0;
|
||||
Vector Ptc(Pc->Width()); Ptc = 1.0;
|
||||
Pc->Mult(Ptc, contact_tdofs);
|
||||
contact_gf.SetFromTrueDofs(contact_tdofs);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << pmesh << contact_gf << flush;
|
||||
}
|
||||
|
||||
if (paraview)
|
||||
{
|
||||
paraview_dc->SetCycle(0);
|
||||
paraview_dc->SetTime(0.0);
|
||||
paraview_dc->Save();
|
||||
}
|
||||
if (visit)
|
||||
{
|
||||
visit_dc->SetCycle(0);
|
||||
visit_dc->SetTime(0.0);
|
||||
visit_dc->Save();
|
||||
}
|
||||
|
||||
if (paraview_dc)
|
||||
{
|
||||
delete paraview_dc;
|
||||
}
|
||||
if (visit_dc)
|
||||
{
|
||||
delete visit_dc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GetEssentialTdofs(const ParFiniteElementSpace & fes, const Array<int> & ess_bdr_attr,
|
||||
const Array<int> & ess_bdr_attr_comp, Array<int> & ess_tdof_list)
|
||||
{
|
||||
ess_tdof_list.SetSize(0);
|
||||
ParMesh * pmesh = fes.GetParMesh();
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
}
|
||||
ess_bdr = 0;
|
||||
Array<int> ess_tdof_list_temp;
|
||||
for (int i = 0; i < ess_bdr_attr.Size(); i++ )
|
||||
{
|
||||
ess_bdr[ess_bdr_attr[i]-1] = 1;
|
||||
fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list_temp, ess_bdr_attr_comp[i]);
|
||||
ess_tdof_list.Append(ess_tdof_list_temp);
|
||||
ess_bdr[ess_bdr_attr[i]-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HypreParMatrix * SetupTribol(ParMesh * pmesh, ParGridFunction * coords,
|
||||
const Array<int> & ess_tdofs, const std::set<int> & mortar_attrs,
|
||||
const std::set<int> & non_mortar_attrs,
|
||||
Vector &gap, double ratio, int tribol_nprocs)
|
||||
{
|
||||
axom::slic::SimpleLogger logger;
|
||||
axom::slic::setIsRoot(mfem::Mpi::Root());
|
||||
|
||||
int coupling_scheme_id = 0;
|
||||
int mesh1_id = 0; int mesh2_id = 1;
|
||||
|
||||
tribol::registerMfemCouplingScheme(
|
||||
coupling_scheme_id, mesh1_id, mesh2_id,
|
||||
*pmesh, *coords, mortar_attrs, non_mortar_attrs,
|
||||
tribol::SURFACE_TO_SURFACE,
|
||||
tribol::NO_SLIDING,
|
||||
tribol::SINGLE_MORTAR,
|
||||
tribol::FRICTIONLESS,
|
||||
tribol::LAGRANGE_MULTIPLIER,
|
||||
tribol::BINNING_GRID
|
||||
);
|
||||
|
||||
tribol::setBinningProximityScale(coupling_scheme_id, ratio);
|
||||
tribol::CouplingSchemeManager::getInstance().findData( coupling_scheme_id )->getParameters().gap_separation_ratio = ratio;
|
||||
// tribol::setMfemLORFactor(coupling_scheme_id, 8);
|
||||
// tribol::setContactAreaFrac( coupling_scheme_id, 1e-8);
|
||||
|
||||
// Access Tribol's pressure grid function (on the contact surface)
|
||||
auto& pressure = tribol::getMfemPressure(coupling_scheme_id);
|
||||
int vsize = pressure.ParFESpace()->GlobalTrueVSize();
|
||||
if (mfem::Mpi::Root())
|
||||
{
|
||||
std::cout << "Number of pressure unknowns: " <<
|
||||
vsize << std::endl;
|
||||
}
|
||||
|
||||
// Set Tribol options for Lagrange multiplier enforcement
|
||||
tribol::setLagrangeMultiplierOptions(
|
||||
coupling_scheme_id,
|
||||
tribol::ImplicitEvalMode::MORTAR_RESIDUAL_JACOBIAN
|
||||
);
|
||||
|
||||
// Update contact mesh decomposition
|
||||
tribol::updateMfemParallelDecomposition(tribol_nprocs);
|
||||
|
||||
// Update contact gaps, forces, and tangent stiffness
|
||||
int cycle = 1; // pseudo cycle
|
||||
double t = 1.0; // pseudo time
|
||||
double dt = 1.0; // pseudo dt
|
||||
tribol::update(cycle, t, dt);
|
||||
|
||||
// tribol::saveRedecompMesh(0);
|
||||
|
||||
// Return contact contribution to the tangent stiffness matrix
|
||||
auto A_blk = tribol::getMfemBlockJacobian(coupling_scheme_id);
|
||||
|
||||
|
||||
|
||||
HypreParMatrix * Mfull = (HypreParMatrix *)(&A_blk->GetBlock(1,0));
|
||||
|
||||
// mfem::out << "Mfull size = " << Mfull->GetGlobalNumRows() << " x " << Mfull->GetGlobalNumCols() << std::endl;
|
||||
|
||||
HypreParMatrix * Me = Mfull->EliminateCols(ess_tdofs);
|
||||
delete Me;
|
||||
|
||||
int h = Mfull->Height();
|
||||
SparseMatrix merged;
|
||||
Mfull->MergeDiagAndOffd(merged);
|
||||
Array<int> nonzero_rows;
|
||||
for (int i = 0; i<h; i++)
|
||||
{
|
||||
if (!merged.RowIsEmpty(i))
|
||||
{
|
||||
nonzero_rows.Append(i);
|
||||
}
|
||||
}
|
||||
|
||||
int hnew = nonzero_rows.Size();
|
||||
|
||||
|
||||
SparseMatrix P(hnew,h);
|
||||
|
||||
for (int i = 0; i<hnew; i++)
|
||||
{
|
||||
int col = nonzero_rows[i];
|
||||
P.Set(i,col,1.0);
|
||||
}
|
||||
P.Finalize();
|
||||
|
||||
SparseMatrix * reduced_merged = Mult(P,merged);
|
||||
|
||||
int rows[2];
|
||||
int cols[2];
|
||||
cols[0] = Mfull->ColPart()[0];
|
||||
cols[1] = Mfull->ColPart()[1];
|
||||
int nrows = reduced_merged->Height();
|
||||
|
||||
int row_offset;
|
||||
MPI_Scan(&nrows,&row_offset,1,MPI_INT,MPI_SUM,Mfull->GetComm());
|
||||
|
||||
row_offset-=nrows;
|
||||
rows[0] = row_offset;
|
||||
rows[1] = row_offset+nrows;
|
||||
int glob_nrows;
|
||||
MPI_Allreduce(&nrows, &glob_nrows,1,MPI_INT,MPI_SUM,Mfull->GetComm());
|
||||
|
||||
int glob_ncols = reduced_merged->Width();
|
||||
HypreParMatrix * M = new HypreParMatrix(Mfull->GetComm(), nrows, glob_nrows,
|
||||
glob_ncols, reduced_merged->GetI(), reduced_merged->GetJ(),
|
||||
reduced_merged->GetData(), rows,cols);
|
||||
delete reduced_merged;
|
||||
|
||||
Vector gap_full;
|
||||
tribol::getMfemGap(coupling_scheme_id, gap_full);
|
||||
|
||||
// mfem::out << "gapsize = " << gap_full.Size() << endl;
|
||||
// mfem::out << "gap norm = " << gap_full.Norml1() << endl;
|
||||
|
||||
// count zeros in gap
|
||||
int gap_nonzeros=0;
|
||||
for (int i = 0; i<gap_full.Size(); i++)
|
||||
{
|
||||
if (gap_full[i] > 1e-15)
|
||||
{
|
||||
gap_nonzeros++;
|
||||
}
|
||||
}
|
||||
// mfem::out << "gap_nonzeros = " << gap_nonzeros << endl;
|
||||
|
||||
auto& P_submesh = *pressure.ParFESpace()->GetProlongationMatrix();
|
||||
Vector gap_true(P_submesh.Width());
|
||||
P_submesh.MultTranspose(gap_full,gap_true);
|
||||
gap.SetSize(nrows);
|
||||
|
||||
for (int i = 0; i<nrows; i++)
|
||||
{
|
||||
gap[i] = gap_true[nonzero_rows[i]];
|
||||
}
|
||||
tribol::finalize();
|
||||
return M;
|
||||
}
|
||||
|
||||
HypreParMatrix * GetContactProlongation(ParFiniteElementSpace * fes,
|
||||
HypreParMatrix *J,
|
||||
Array<int> ess_tdof_list)
|
||||
{
|
||||
HypreParMatrix * Jt = J->Transpose();
|
||||
Jt->EliminateRows(ess_tdof_list);
|
||||
int hJt = Jt->Height();
|
||||
SparseMatrix mergedJt;
|
||||
Jt->MergeDiagAndOffd(mergedJt);
|
||||
Array<int> nonzerorows;
|
||||
for (int i = 0; i<hJt; i++)
|
||||
{
|
||||
if (!mergedJt.RowIsEmpty(i))
|
||||
{
|
||||
nonzerorows.Append(i);
|
||||
}
|
||||
}
|
||||
int hc = nonzerorows.Size();
|
||||
SparseMatrix Pct(hc,fes->GlobalTrueVSize());
|
||||
|
||||
// mfem::out << "number of nonzero cols (contact dofs) = " << hc << std::endl;
|
||||
|
||||
for (int i = 0; i<hc; i++)
|
||||
{
|
||||
int col = nonzerorows[i]+fes->GetMyTDofOffset();
|
||||
Pct.Set(i,col,1.0);
|
||||
}
|
||||
Pct.Finalize();
|
||||
|
||||
int rows_c[2];
|
||||
int cols_c[2];
|
||||
int nrows_c = Pct.Height();
|
||||
|
||||
int row_offset_c;
|
||||
MPI_Scan(&nrows_c,&row_offset_c,1,MPI_INT,MPI_SUM,J->GetComm());
|
||||
|
||||
row_offset_c-=nrows_c;
|
||||
rows_c[0] = row_offset_c;
|
||||
rows_c[1] = row_offset_c+nrows_c;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
cols_c[i] = fes->GetTrueDofOffsets()[i];
|
||||
}
|
||||
int glob_nrows_c;
|
||||
int glob_ncols_c = fes->GlobalTrueVSize();
|
||||
MPI_Allreduce(&nrows_c, &glob_nrows_c,1,MPI_INT,MPI_SUM,J->GetComm());
|
||||
|
||||
HypreParMatrix * P_ct = new HypreParMatrix(J->GetComm(), nrows_c, glob_nrows_c,
|
||||
glob_ncols_c, Pct.GetI(), Pct.GetJ(),
|
||||
Pct.GetData(), rows_c,cols_c);
|
||||
HypreParMatrix * Pc = P_ct->Transpose();
|
||||
delete P_ct;
|
||||
return Pc;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user