Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17e11faf07 | ||
|
|
2a482c0c9e |
@@ -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
|
||||
|
||||
+5
-36
@@ -3722,37 +3722,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 +3738,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 +3752,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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+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
|
||||
|
||||
|
||||
@@ -37,3 +37,4 @@ add_subdirectory(tribol)
|
||||
add_subdirectory(hooke)
|
||||
add_subdirectory(dpg)
|
||||
add_subdirectory(hdiv-linear-solver)
|
||||
add_subdirectory(interiorpointsolver)
|
||||
|
||||
@@ -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,964 @@
|
||||
#include "mfem.hpp"
|
||||
#include "IPsolver.hpp"
|
||||
#include "Problem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
ParInteriorPointSolver::ParInteriorPointSolver(ParGeneralOptProblem * problem_)
|
||||
: problem(problem_),
|
||||
block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
|
||||
Huu(nullptr), Hum(nullptr), Hmu(nullptr),
|
||||
Hmm(nullptr), Wmm(nullptr), D(nullptr),
|
||||
Ju(nullptr), Jm(nullptr), JuT(nullptr), JmT(nullptr),
|
||||
saveIterates(false)
|
||||
{
|
||||
OptTol = 1.e-2;
|
||||
max_iter = 20;
|
||||
mu_k = 1.0;
|
||||
|
||||
sMax = 1.e2;
|
||||
kSig = 1.e10; // control deviation from primal Hessian
|
||||
tauMin = 0.99; // control rate at which iterates can approach the boundary
|
||||
eta = 1.e-4; // backtracking constant
|
||||
thetaMin = 1.e-4; // allowed violation of the equality constraints
|
||||
|
||||
// constants in line-step A-5.4
|
||||
delta = 1.0;
|
||||
sTheta = 1.1;
|
||||
sPhi = 2.3;
|
||||
|
||||
// control the rate at which the penalty parameter is decreased
|
||||
kMu = 0.2;
|
||||
thetaMu = 1.5;
|
||||
|
||||
thetaMax = 1.e6; // maximum constraint violation
|
||||
// data for the second order correction
|
||||
kSoc = 0.99;
|
||||
|
||||
// equation (18)
|
||||
gTheta = 1.e-5;
|
||||
gPhi = 1.e-5;
|
||||
|
||||
kEps = 1.e1;
|
||||
|
||||
dimU = problem->GetDimU();
|
||||
dimM = problem->GetDimM();
|
||||
dimC = problem->GetDimC();
|
||||
ckSoc.SetSize(dimC);
|
||||
|
||||
block_offsetsumlz[0] = 0;
|
||||
block_offsetsumlz[1] = dimU; // u
|
||||
block_offsetsumlz[2] = dimM; // m
|
||||
block_offsetsumlz[3] = dimC; // lambda
|
||||
block_offsetsumlz[4] = dimM; // zl
|
||||
block_offsetsumlz.PartialSum();
|
||||
|
||||
MPI_Allreduce(&dimU, &dimUGlb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(&dimM, &dimMGlb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(&dimC, &dimCGlb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
|
||||
for(int i = 0; i < block_offsetsuml.Size(); i++)
|
||||
{
|
||||
block_offsetsuml[i] = block_offsetsumlz[i];
|
||||
}
|
||||
for(int i = 0; i < block_offsetsx.Size(); i++)
|
||||
{
|
||||
block_offsetsx[i] = block_offsetsuml[i] ;
|
||||
}
|
||||
|
||||
|
||||
ml = problem->Getml();
|
||||
|
||||
lk.SetSize(dimC); lk = 0.0;
|
||||
zlk.SetSize(dimM); zlk = 0.0;
|
||||
|
||||
linSolver = 0;
|
||||
linSolveTol = 1.e-8;
|
||||
MyRank = Mpi::WorldRank();
|
||||
iAmRoot = MyRank == 0 ? true : false;
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
|
||||
{
|
||||
double alphaMaxloc = 1.0;
|
||||
double alphaTmp;
|
||||
for(int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
if( xhat(i) < 0. )
|
||||
{
|
||||
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
|
||||
alphaMaxloc = min(alphaMaxloc, alphaTmp);
|
||||
}
|
||||
}
|
||||
|
||||
// alphaMaxloc is the local maximum step size which is
|
||||
// distinct on each MPI process. Need to compute
|
||||
// the global maximum step size
|
||||
double alphaMaxglb;
|
||||
MPI_Allreduce(&alphaMaxloc, &alphaMaxglb, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
|
||||
return alphaMaxglb;
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
|
||||
{
|
||||
Vector zero(x.Size()); zero = 0.0;
|
||||
return MaxStepSize(x, zero, xhat, tau);
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::Mult(const Vector &x0, Vector &xf)
|
||||
{
|
||||
BlockVector x0block(block_offsetsx); x0block = 0.0;
|
||||
x0block.GetBlock(0).Set(1.0, x0);
|
||||
|
||||
|
||||
ParOptProblem * OptProblem = dynamic_cast<ParOptProblem *>(problem);
|
||||
if (dimM > 0)
|
||||
{
|
||||
if (true)//OptProblem == nullptr)
|
||||
{
|
||||
// fixed initialization
|
||||
x0block.GetBlock(1) = 1.0;
|
||||
x0block.GetBlock(1).Add(1.0, ml);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use g(d) - s = 0
|
||||
x0block.GetBlock(1) = 0.0;
|
||||
x0block.GetBlock(1).Add(1.0, ml);
|
||||
Vector c0(dimC); c0 = 0.0;
|
||||
problem->c(x0block, c0);
|
||||
Vector dm(dimM); dm = 0.0;
|
||||
for (int i = 0; i < dimM; i++)
|
||||
{
|
||||
dm(i) = max(1.e0 - x0block(dimU + i), c0(i));
|
||||
}
|
||||
x0block.GetBlock(1).Add(1.0, dm);
|
||||
}
|
||||
}
|
||||
|
||||
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
|
||||
Mult(x0block, xfblock);
|
||||
xf.Set(1.0, xfblock.GetBlock(0));
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
|
||||
{
|
||||
converged = false;
|
||||
|
||||
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
|
||||
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
|
||||
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
|
||||
Vector zlhat(dimM); zlhat = 0.0;
|
||||
|
||||
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
|
||||
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
|
||||
|
||||
// running estimate of the final values of the Lagrange multipliers
|
||||
lk = 0.0;
|
||||
zlk = 0.0;
|
||||
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
|
||||
}
|
||||
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
/* set theta0 = theta(x0)
|
||||
* thetaMin
|
||||
* thetaMax
|
||||
* when theta(xk) < thetaMin and the switching condition holds
|
||||
* then we ask for the Armijo sufficient decrease of the barrier
|
||||
* objective to be satisfied, in order to accept the trial step length alphakl
|
||||
*
|
||||
* thetaMax controls how the filter is initialized for each log-barrier subproblem
|
||||
* F0 = {(th, phi) s.t. th > thetaMax}
|
||||
* that is the filter does not allow for iterates where the constraint violation
|
||||
* is larger than that of thetaMax
|
||||
*/
|
||||
double theta0 = theta(xk);
|
||||
thetaMin = 1.e-4 * max(1.0, theta0);
|
||||
thetaMax = 1.e8 * thetaMin; // 1.e4 * max(1.0, theta0)
|
||||
|
||||
double Eeval, maxBarrierSolves, Eevalmu0;
|
||||
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
|
||||
|
||||
maxBarrierSolves = 10;
|
||||
bool smallStep;
|
||||
int numSmallSteps = 0;
|
||||
for(jOpt = 0; jOpt < max_iter; jOpt++)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "interior-point solve step " << jOpt << endl;
|
||||
}
|
||||
// A-2. Check convergence of overall optimization problem
|
||||
printOptimalityError = false;
|
||||
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
|
||||
if(Eevalmu0 < OptTol)
|
||||
{
|
||||
converged = true;
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "solved optimization problem :)\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(jOpt > 0) { maxBarrierSolves = 1; }
|
||||
|
||||
for(int i = 0; i < maxBarrierSolves; i++)
|
||||
{
|
||||
// A-3. Check convergence of the barrier subproblem
|
||||
printOptimalityError = true;
|
||||
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "E = " << Eeval << endl;
|
||||
}
|
||||
if(Eeval < kEps * mu_k)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "solved barrier subproblem :), for mu = " << mu_k << endl;
|
||||
}
|
||||
// A-3.1. Recompute the barrier parameter
|
||||
mu_k = max(OptTol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
|
||||
// A-3.2. Re-initialize the filter
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// A-4. Compute the search direction
|
||||
// solve for (uhat, mhat, lhat)
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "\n** A-4. IP-Newton solve **\n";
|
||||
}
|
||||
zlhat = 0.0; Xhatuml = 0.0;
|
||||
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, smallStep, mu_k, false);
|
||||
if (smallStep && numSmallSteps < 5)
|
||||
{
|
||||
numSmallSteps += 1;
|
||||
xk.GetBlock(0).Add(1.0, Xhatuml.GetBlock(0));
|
||||
xk.GetBlock(1).Add(1.0, Xhatuml.GetBlock(1));
|
||||
lk.Add(1.0, Xhatuml.GetBlock(2));
|
||||
zlk.Add(1.0, zlhat);
|
||||
continue;
|
||||
}
|
||||
numSmallSteps = 0;
|
||||
|
||||
// assign data stack, X = (u, m, l, zl)
|
||||
Xk = 0.0;
|
||||
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
Xk.GetBlock(2).Set(1.0, lk);
|
||||
Xk.GetBlock(3).Set(1.0, zlk);
|
||||
|
||||
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
|
||||
Xhat = 0.0;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
|
||||
}
|
||||
Xhat.GetBlock(3).Set(1.0, zlhat);
|
||||
|
||||
// A-5. Backtracking line search.
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "\n** A-5. Linesearch **\n";
|
||||
cout << "mu = " << mu_k << endl;
|
||||
}
|
||||
lineSearch(Xk, Xhat, mu_k);
|
||||
|
||||
if(lineSearchSuccess)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "lineSearch successful :)\n";
|
||||
}
|
||||
if(!switchCondition || !sufficientDecrease)
|
||||
{
|
||||
F1.Append( (1. - gTheta) * thx0);
|
||||
F2.Append( phx0 - gPhi * thx0);
|
||||
}
|
||||
// ----- A-6: Accept the trial point
|
||||
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
|
||||
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
|
||||
lk.Add(alpha, Xhat.GetBlock(2));
|
||||
zlk.Add(alphaz, Xhat.GetBlock(3));
|
||||
projectZ(xk, zlk, mu_k);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "lineSearch not successful :(\n";
|
||||
cout << "attempting feasibility restoration with theta = " << thx0 << endl;
|
||||
}
|
||||
FeasibilityRestoration(xk, lk, zlk, Xk, mu_k);
|
||||
xk.GetBlock(0).Set(1.0, Xk.GetBlock(0));
|
||||
xk.GetBlock(1).Set(1.0, Xk.GetBlock(1));
|
||||
lk.Set(1.0, Xk.GetBlock(2));
|
||||
zlk.Set(1.0, Xk.GetBlock(3));
|
||||
}
|
||||
if(jOpt + 1 == max_iter && iAmRoot)
|
||||
{
|
||||
cout << "maximum optimization iterations :(\n";
|
||||
}
|
||||
}
|
||||
// done with optimization routine, just reassign data to xf reference so
|
||||
// that the application code has access to the optimal point
|
||||
xf = 0.0;
|
||||
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
|
||||
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
|
||||
{
|
||||
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
|
||||
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
|
||||
|
||||
Huu = problem->Duuf(x);
|
||||
Hum = problem->Dumf(x);
|
||||
Hmu = problem->Dmuf(x);
|
||||
Hmm = problem->Dmmf(x);
|
||||
|
||||
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
|
||||
}
|
||||
|
||||
if (saveIterates)
|
||||
{
|
||||
std::ofstream diagStream;
|
||||
char diagString[100];
|
||||
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
|
||||
diagStream.open(diagString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
|
||||
}
|
||||
diagStream.close();
|
||||
|
||||
std::ofstream sStream;
|
||||
char sString[100];
|
||||
snprintf(sString, 100, "logBarrierHessiandata/s%d.dat", jOpt);
|
||||
sStream.open(sString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
sStream << setprecision(30) << x(ii+dimU) << endl;
|
||||
}
|
||||
sStream.close();
|
||||
|
||||
std::ofstream lStream;
|
||||
char lString[100];
|
||||
snprintf(lString, 100, "logBarrierHessiandata/l%d.dat", jOpt);
|
||||
lStream.open(lString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
lStream << setprecision(30) << l(ii) << endl;
|
||||
}
|
||||
lStream.close();
|
||||
|
||||
std::ofstream zlStream;
|
||||
char zlString[100];
|
||||
snprintf(zlString, 100, "logBarrierHessiandata/zl%d.dat", jOpt);
|
||||
zlStream.open(zlString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
zlStream << setprecision(30) << zl(ii) << endl;
|
||||
}
|
||||
zlStream.close();
|
||||
|
||||
std::ofstream dStream;
|
||||
char dString[100];
|
||||
snprintf(dString, 100, "logBarrierHessiandata/d%d.dat", jOpt);
|
||||
dStream.open(dString, ios::out | ios::trunc);
|
||||
for(int ii = 0; ii < dimU; ii++)
|
||||
{
|
||||
dStream << setprecision(30) << x(ii) << endl;
|
||||
}
|
||||
dStream.close();
|
||||
}
|
||||
|
||||
D = GenerateHypreParMatrixFromDiagonal(problem->GetDofOffsetsM(), DiagLogBar);
|
||||
|
||||
if(Hmm != nullptr)
|
||||
{
|
||||
Wmm = Hmm;
|
||||
Wmm->Add(1.0, *D);
|
||||
}
|
||||
else
|
||||
{
|
||||
Wmm = D;
|
||||
}
|
||||
|
||||
Ju = problem->Duc(x); JuT = Ju->Transpose();
|
||||
Jm = problem->Dmc(x); JmT = Jm->Transpose();
|
||||
// IP-Newton system matrix
|
||||
// Ak = [[H_(u,u) H_(u,m) J_u^T]
|
||||
// [H_(m,u) W_(m,m) J_m^T]
|
||||
// [ J_u J_m 0 ]]
|
||||
|
||||
Ak.SetBlock(0, 0, Huu); Ak.SetBlock(0, 2, JuT);
|
||||
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
|
||||
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
|
||||
|
||||
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
|
||||
}
|
||||
|
||||
// perturbed KKT system solve
|
||||
// determine the search direction
|
||||
void ParInteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, bool & smallStep, double mu, bool socSolve)
|
||||
{
|
||||
// solve A x = b, where A is the IP-Newton matrix
|
||||
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
|
||||
FormIPNewtonMat(x, l, zl, A);
|
||||
|
||||
// [grad_u phi + Ju^T l]
|
||||
// b = - [grad_m phi + Jm^T l]
|
||||
// [ c ]
|
||||
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
|
||||
BlockVector JTl(block_offsetsx); JTl = 0.0;
|
||||
Dxphi(x, mu, gradphi);
|
||||
|
||||
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
|
||||
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
|
||||
|
||||
for(int ii = 0; ii < 2; ii++)
|
||||
{
|
||||
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
|
||||
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
|
||||
}
|
||||
if(!socSolve)
|
||||
{
|
||||
problem->c(x, b.GetBlock(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
b.GetBlock(2).Set(1.0, ckSoc);
|
||||
}
|
||||
b *= -1.0;
|
||||
Xhat = 0.0;
|
||||
|
||||
|
||||
// Direct solver (default)
|
||||
if(linSolver == 0)
|
||||
{
|
||||
Array2D<HypreParMatrix *> ABlockMatrix(3,3);
|
||||
for(int ii = 0; ii < 3; ii++)
|
||||
{
|
||||
for(int jj = 0; jj < 3; jj++)
|
||||
{
|
||||
if(!A.IsZeroBlock(ii, jj))
|
||||
{
|
||||
ABlockMatrix(ii, jj) = dynamic_cast<HypreParMatrix *>(const_cast<Operator *>(&(A.GetBlock(ii, jj))));
|
||||
}
|
||||
else
|
||||
{
|
||||
ABlockMatrix(ii, jj) = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HypreParMatrix * Ah = HypreParMatrixFromBlocks(ABlockMatrix);
|
||||
|
||||
/* direct solve of the 3x3 IP-Newton linear system */
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
MUMPSSolver ASolver;
|
||||
ASolver.SetPrintLevel(0);
|
||||
ASolver.SetMatrixSymType(MUMPSSolver::MatType::SYMMETRIC_INDEFINITE);
|
||||
ASolver.SetOperator(*Ah);
|
||||
ASolver.Mult(b, Xhat);
|
||||
#else
|
||||
#ifdef MFEM_USE_MKL_CPARDISO
|
||||
CPardisoSolver ASolver(MPI_COMM_WORLD);
|
||||
ASolver.SetOperator(*Ah);
|
||||
ASolver.Mult(b, Xhat);
|
||||
#else
|
||||
MFEM_VERIFY(false, "linSolver 0 will not work unless compiled with MUMPS or MKL");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
delete Ah;
|
||||
}
|
||||
else if(linSolver == 1 || linSolver == 2)
|
||||
{
|
||||
|
||||
// assuming Jm = -I and Hum = 0, Hmu = 0, Hmm = 0
|
||||
ParOptProblem * tempProblem = dynamic_cast<ParOptProblem *>(problem);
|
||||
MFEM_VERIFY(tempProblem != nullptr, "linSolver option 1 and 2 are only applicable to ParOptProblem's");
|
||||
|
||||
|
||||
|
||||
// form A = Huu + Ju^T D Ju, Wmm = D for contact
|
||||
HypreParMatrix * Huuloc = dynamic_cast<HypreParMatrix *>(const_cast<Operator *>(&(A.GetBlock(0, 0))));
|
||||
HypreParMatrix * Wmmloc = dynamic_cast<HypreParMatrix *>(const_cast<Operator *>(&(A.GetBlock(1, 1))));
|
||||
HypreParMatrix * Juloc = dynamic_cast<HypreParMatrix *>(const_cast<Operator *>(&(A.GetBlock(2, 0))));
|
||||
HypreParMatrix * JuTloc = dynamic_cast<HypreParMatrix *>(const_cast<Operator *>(&(A.GetBlock(0, 2))));
|
||||
|
||||
|
||||
HypreParMatrix *JuTDJu = RAP(Wmmloc, Juloc); // Ju^T D Ju
|
||||
HypreParMatrix *Areduced = ParAdd(Huuloc, JuTDJu); // Huu + Ju^T D Ju
|
||||
/* prepare the reduced rhs
|
||||
* breduced = bu + Ju^T (bm + Wmm bl) */
|
||||
Vector breduced(dimU); breduced = 0.0;
|
||||
Vector tempVec(dimM); tempVec = 0.0;
|
||||
Wmmloc->Mult(b.GetBlock(2), tempVec);
|
||||
tempVec.Add(1.0, b.GetBlock(1));
|
||||
JuTloc->Mult(tempVec, breduced);
|
||||
breduced.Add(1.0, b.GetBlock(0));
|
||||
|
||||
if(linSolver == 1)
|
||||
{
|
||||
// setup the solver for the reduced linear system
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
MUMPSSolver AreducedSolver;
|
||||
AreducedSolver.SetPrintLevel(0);
|
||||
AreducedSolver.SetMatrixSymType(MUMPSSolver::MatType::SYMMETRIC_INDEFINITE);
|
||||
AreducedSolver.SetOperator(*Areduced);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
#else
|
||||
#ifdef MFEM_USE_MKL_CPARDISO
|
||||
CPardisoSolver AreducedSolver(MPI_COMM_WORLD);
|
||||
AreducedSolver.SetOperator(*Areduced);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
#else
|
||||
MFEM_VERIFY(false, "linSolver 1 will not work unless compiled with MUMPS or MKL");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
HyprePCG AreducedSolver(MPI_COMM_WORLD);
|
||||
AreducedSolver.SetOperator(*Areduced);
|
||||
HypreBoomerAMG AreducedPrec;
|
||||
AreducedSolver.SetTol(linSolveTol);
|
||||
AreducedSolver.SetMaxIter(500);
|
||||
AreducedSolver.SetPreconditioner(AreducedPrec);
|
||||
AreducedSolver.SetResidualConvergenceOptions(); // convergence criteria based on residual norm
|
||||
AreducedSolver.SetPrintLevel(2);
|
||||
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
|
||||
}
|
||||
|
||||
// now propagate solved uhat to obtain mhat and lhat
|
||||
// xm = Ju xu - bl
|
||||
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
|
||||
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
|
||||
|
||||
// xl = Wmm xm - bm
|
||||
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
|
||||
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
|
||||
|
||||
delete JuTDJu;
|
||||
delete Areduced;
|
||||
}
|
||||
|
||||
/* backsolve to determine zlhat */
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
|
||||
}
|
||||
|
||||
Vector smallStepCheckVec(dimU + dimM); smallStepCheckVec = 0.0;
|
||||
for(int ii = 0; ii < dimU + dimM; ii++)
|
||||
{
|
||||
smallStepCheckVec(ii) = abs(Xhat(ii)) / (1. + abs(x(ii)));
|
||||
}
|
||||
double smallStepCheckVal = GlobalLpNorm(infinity(), smallStepCheckVec.Normlinf(), MPI_COMM_WORLD);
|
||||
smallStep = (smallStepCheckVal < 1.e-15) ? true : false;
|
||||
if (smallStep && iAmRoot)
|
||||
{
|
||||
cout << "SMALL STEP\n";
|
||||
}
|
||||
|
||||
|
||||
// free memory
|
||||
delete D;
|
||||
delete JuT;
|
||||
delete JmT;
|
||||
if(Hmm != nullptr)
|
||||
{
|
||||
delete Wmm;
|
||||
}
|
||||
}
|
||||
|
||||
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
|
||||
|
||||
void ParInteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
|
||||
{
|
||||
double tau = tauMin;
|
||||
Vector u0 = X0.GetBlock(0);
|
||||
Vector m0 = X0.GetBlock(1);
|
||||
Vector l0 = X0.GetBlock(2);
|
||||
Vector z0 = X0.GetBlock(3);
|
||||
Vector uhat = Xhat.GetBlock(0);
|
||||
Vector mhat = Xhat.GetBlock(1);
|
||||
Vector lhat = Xhat.GetBlock(2);
|
||||
Vector zhat = Xhat.GetBlock(3);
|
||||
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
|
||||
double alphaMaxz = MaxStepSize(z0, zhat, tau);
|
||||
alphaz = alphaMaxz;
|
||||
|
||||
BlockVector x0(block_offsetsx); x0 = 0.0;
|
||||
x0.GetBlock(0).Set(1.0, u0);
|
||||
x0.GetBlock(1).Set(1.0, m0);
|
||||
|
||||
BlockVector xhat(block_offsetsx); xhat = 0.0;
|
||||
xhat.GetBlock(0).Set(1.0, uhat);
|
||||
xhat.GetBlock(1).Set(1.0, mhat);
|
||||
|
||||
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
|
||||
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
|
||||
int maxBacktrack = 20;
|
||||
alpha = alphaMax;
|
||||
|
||||
Vector ck0(dimC); ck0 = 0.0;
|
||||
Vector zhatsoc(dimM); zhatsoc = 0.0;
|
||||
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
|
||||
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
|
||||
Vector uhatsoc(dimU); uhatsoc = 0.0;
|
||||
Vector mhatsoc(dimM); mhatsoc = 0.0;
|
||||
|
||||
Dxphi(x0, mu, Dxphi0);
|
||||
|
||||
Dxphi0_xhat = InnerProduct(MPI_COMM_WORLD, Dxphi0, xhat);
|
||||
descentDirection = Dxphi0_xhat < 0. ? true : false;
|
||||
if(descentDirection)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "is a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "is not a descent direction for the log-barrier objective\n";
|
||||
}
|
||||
thx0 = theta(x0);
|
||||
phx0 = phi(x0, mu);
|
||||
|
||||
lineSearchSuccess = false;
|
||||
for(int i = 0; i < maxBacktrack; i++)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "\n--------- alpha = " << alpha << " ---------\n";
|
||||
}
|
||||
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
|
||||
xtrial.Set(1.0, x0);
|
||||
xtrial.Add(alpha, xhat);
|
||||
|
||||
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
|
||||
thxtrial = theta(xtrial);
|
||||
phxtrial = phi(xtrial, mu);
|
||||
filterCheck(thxtrial, phxtrial);
|
||||
if (!inFilterRegion)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "not in filter region :)\n";
|
||||
}
|
||||
// ------ A.5.4: Check sufficient decrease
|
||||
if(!descentDirection)
|
||||
{
|
||||
switchCondition = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
|
||||
}
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
|
||||
cout << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
|
||||
cout << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
|
||||
}
|
||||
// Case I
|
||||
if(thx0 <= thetaMin && switchCondition)
|
||||
{
|
||||
sufficientDecrease = (phxtrial <= phx0 + eta * alpha * Dxphi0_xhat) ? true : false;
|
||||
if(sufficientDecrease)
|
||||
{
|
||||
if(iAmRoot) { cout << "Line search successful: sufficient decrease in log-barrier objective.\n"; }
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
|
||||
{
|
||||
if(iAmRoot) { cout << "Line search successful: infeasibility or log-barrier objective decreased.\n"; }
|
||||
// accept the trial step
|
||||
lineSearchSuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "in filter region :(\n";
|
||||
}
|
||||
}
|
||||
alpha *= 0.5;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
|
||||
{
|
||||
double zi;
|
||||
double mudivmml;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
zi = z(i);
|
||||
mudivmml = mu / (x(i + dimU) - ml(i));
|
||||
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
|
||||
}
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::filterCheck(double th, double ph)
|
||||
{
|
||||
inFilterRegion = false;
|
||||
if(th > thetaMax)
|
||||
{
|
||||
inFilterRegion = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < F1.Size(); i++)
|
||||
{
|
||||
if(th >= F1[i] && ph >= F2[i])
|
||||
{
|
||||
inFilterRegion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::FeasibilityRestoration(const BlockVector & x, const Vector &l, const Vector &zl, BlockVector &X, double mu)
|
||||
{
|
||||
ParOptProblem * OptProblem = dynamic_cast<ParOptProblem *>(problem);
|
||||
X.GetBlock(0).Set(1.0, x.GetBlock(0));
|
||||
X.GetBlock(1).Set(1.0, x.GetBlock(1));
|
||||
X.GetBlock(2).Set(1.0, l);
|
||||
X.GetBlock(3).Set(1.0, zl);
|
||||
|
||||
if (OptProblem != nullptr)
|
||||
{
|
||||
Vector g(dimC); g = 0.0;
|
||||
OptProblem->g(x.GetBlock(0), g);
|
||||
Ju = OptProblem->Ddg(x.GetBlock(0));
|
||||
Ju->DropSmallEntries(1.e-16);
|
||||
SparseMatrix JuMerged;
|
||||
Ju->MergeDiagAndOffd(JuMerged);
|
||||
|
||||
int num_loc_modified_constraints = 0;
|
||||
int num_glb_modified_constraints = 0;
|
||||
for (int i = 0; i < dimC; i++)
|
||||
{
|
||||
if(JuMerged.RowIsEmpty(i))
|
||||
{
|
||||
if (g(i) < 1.e-15)
|
||||
{
|
||||
if (iAmRoot)
|
||||
{
|
||||
cout << "WARNING: LICQ violation detected (g_i and grad(g_i) both zero)\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
X(dimU + i) = g(i); // s_i = \gamma
|
||||
X(dimU + dimM + i) = -1. * mu / g(i); // l_i = -mu/\gamma
|
||||
X(dimU + dimM + dimC + i) = mu / g(i);
|
||||
num_loc_modified_constraints += 1;
|
||||
}
|
||||
}
|
||||
MPI_Allreduce(&num_loc_modified_constraints, &num_glb_modified_constraints, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
if (num_glb_modified_constraints == 0)
|
||||
{
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "trying feasibility restoration with no null rows in Jacobian\n";
|
||||
cout << "exiting\n";
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool printEeval)
|
||||
{
|
||||
double E1, E2, E3;
|
||||
double sc, sd;
|
||||
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
|
||||
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
|
||||
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
|
||||
|
||||
DxL(x, l, zl, gradL);
|
||||
E1 = GlobalLpNorm(infinity(), gradL.Normlinf(), MPI_COMM_WORLD);
|
||||
|
||||
problem->c(x, cx);
|
||||
E2 = GlobalLpNorm(infinity(), cx.Normlinf(), MPI_COMM_WORLD);
|
||||
|
||||
for(int ii = 0; ii < dimM; ii++)
|
||||
{
|
||||
comp(ii) = x(dimU + ii) * zl(ii) - mu;
|
||||
}
|
||||
E3 = GlobalLpNorm(infinity(), comp.Normlinf(), MPI_COMM_WORLD);
|
||||
double ll1, zl1;
|
||||
|
||||
zl1 = GlobalLpNorm(1, zl.Norml1(), MPI_COMM_WORLD);
|
||||
ll1 = GlobalLpNorm(1, l.Norml1(), MPI_COMM_WORLD);
|
||||
sc = max(sMax, zl1 / (double(dimMGlb)) ) / sMax;
|
||||
sd = max(sMax, (ll1 + zl1) / (double(dimCGlb + dimMGlb))) / sMax;
|
||||
if(iAmRoot)
|
||||
{
|
||||
cout << "evaluating optimality error for mu = " << mu << endl;
|
||||
cout << "stationarity measure = " << E1 / sd << endl;
|
||||
cout << "feasibility measure = " << E2 << endl;
|
||||
cout << "complimentarity measure = " << E3 / sc << endl;
|
||||
}
|
||||
return max(max(E1 / sd, E2), E3 / sc);
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool printEeval)
|
||||
{
|
||||
return E(x, l, zl, 0.0, printEeval);
|
||||
}
|
||||
|
||||
double ParInteriorPointSolver::theta(const BlockVector &x)
|
||||
{
|
||||
Vector cx(dimC); cx = 0.0;
|
||||
problem->c(x, cx);
|
||||
return GlobalLpNorm(2, cx.Norml2(), MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
// log-barrier objective
|
||||
double ParInteriorPointSolver::phi(const BlockVector &x, double mu)
|
||||
{
|
||||
double fx = problem->CalcObjective(x);
|
||||
double logBarrierLoc = 0.0;
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
logBarrierLoc += log(x(dimU+i) - ml(i));
|
||||
}
|
||||
double logBarrierGlb;
|
||||
MPI_Allreduce(&logBarrierLoc, &logBarrierGlb, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
|
||||
return fx - mu * logBarrierGlb;
|
||||
}
|
||||
|
||||
// gradient of log-barrier objective with respect to x = (u, m)
|
||||
void ParInteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
|
||||
{
|
||||
problem->CalcObjectiveGrad(x, y);
|
||||
|
||||
for(int i = 0; i < dimM; i++)
|
||||
{
|
||||
y(dimU + i) -= mu / (x(dimU + i));
|
||||
}
|
||||
}
|
||||
|
||||
// Lagrangian function evaluation
|
||||
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
|
||||
double ParInteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
|
||||
{
|
||||
double fx = problem->CalcObjective(x);
|
||||
Vector cx(dimC); problem->c(x, cx);
|
||||
return (fx + InnerProduct(MPI_COMM_WORLD, cx, l) - InnerProduct(MPI_COMM_WORLD, x.GetBlock(1), zl));
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
|
||||
{
|
||||
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
|
||||
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
|
||||
problem->CalcObjectiveGrad(x, gradxf);
|
||||
|
||||
HypreParMatrix *Jacu, *Jacm;
|
||||
Jacu = problem->Duc(x);
|
||||
Jacm = problem->Dmc(x);
|
||||
Jacu->MultTranspose(l, y.GetBlock(0));
|
||||
Jacm->MultTranspose(l, y.GetBlock(1));
|
||||
|
||||
y.Add(1.0, gradxf);
|
||||
(y.GetBlock(1)).Add(-1.0, zl);
|
||||
}
|
||||
|
||||
bool ParInteriorPointSolver::GetConverged() const
|
||||
{
|
||||
return converged;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetTol(double Tol)
|
||||
{
|
||||
OptTol = Tol;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetMaxIter(int max_it)
|
||||
{
|
||||
max_iter = max_it;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetBarrierParameter(double mu_0)
|
||||
{
|
||||
mu_k = mu_0;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SaveIterates(bool save)
|
||||
{
|
||||
saveIterates = save;
|
||||
}
|
||||
|
||||
|
||||
void ParInteriorPointSolver::SetLinearSolver(int LinSolver)
|
||||
{
|
||||
linSolver = LinSolver;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::SetLinearSolveTol(double Tol)
|
||||
{
|
||||
linSolveTol = Tol;
|
||||
}
|
||||
|
||||
void ParInteriorPointSolver::GetLagrangeMultiplier(Vector & y)
|
||||
{
|
||||
y.SetSize(dimM); y = 0.;
|
||||
y.Set(1.0, zlk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParInteriorPointSolver::~ParInteriorPointSolver()
|
||||
{
|
||||
F1.DeleteAll();
|
||||
F2.DeleteAll();
|
||||
block_offsetsx.DeleteAll();
|
||||
block_offsetsumlz.DeleteAll();
|
||||
block_offsetsuml.DeleteAll();
|
||||
ml.SetSize(0);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "mfem.hpp"
|
||||
#include "Problem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
#ifndef PARIPSOLVER
|
||||
#define PARIPSOLVER
|
||||
|
||||
class ParInteriorPointSolver
|
||||
{
|
||||
protected:
|
||||
ParGeneralOptProblem* problem;
|
||||
double OptTol;
|
||||
int max_iter;
|
||||
double mu_k; // \mu_k
|
||||
Vector lk, zlk;
|
||||
|
||||
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
|
||||
double thetaMax, kSoc, gTheta, gPhi, kEps;
|
||||
|
||||
// filter
|
||||
Array<double> F1, F2;
|
||||
|
||||
// quantities computed in lineSearch
|
||||
double alpha, alphaz;
|
||||
double thx0, thxtrial;
|
||||
double phx0, phxtrial;
|
||||
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
|
||||
double Dxphi0_xhat;
|
||||
|
||||
int dimU, dimM, dimC;
|
||||
int dimUGlb, dimMGlb, dimCGlb;
|
||||
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
|
||||
Vector ml;
|
||||
|
||||
Vector ckSoc;
|
||||
HypreParMatrix * Huu, * Hum, * Hmu, * Hmm, * Wmm, *D, * Ju, * Jm, * JuT, * JmT;
|
||||
|
||||
int jOpt;
|
||||
bool converged;
|
||||
|
||||
int MyRank;
|
||||
bool iAmRoot;
|
||||
|
||||
bool saveLogBarrierIterates;
|
||||
bool saveIterates;
|
||||
int linSolver;
|
||||
double linSolveTol;
|
||||
public:
|
||||
ParInteriorPointSolver(ParGeneralOptProblem*);
|
||||
double MaxStepSize(Vector& , Vector& , Vector& , double);
|
||||
double MaxStepSize(Vector& , Vector& , double);
|
||||
void Mult(const BlockVector& , BlockVector&);
|
||||
void Mult(const Vector&, Vector &);
|
||||
void GetLagrangeMultiplier(Vector &);
|
||||
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
|
||||
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , bool &, double, bool);
|
||||
void lineSearch(BlockVector& , BlockVector& , double);
|
||||
void projectZ(const Vector & , Vector &, double);
|
||||
void filterCheck(double, double);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
|
||||
double E(const BlockVector &, const Vector &, const Vector &, bool);
|
||||
bool GetConverged() const;
|
||||
double theta(const BlockVector &);
|
||||
double phi(const BlockVector &, double);
|
||||
void Dxphi(const BlockVector &, double, BlockVector &);
|
||||
double L(const BlockVector &, const Vector &, const Vector &);
|
||||
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
|
||||
void SetTol(double);
|
||||
void SetMaxIter(int);
|
||||
void SetBarrierParameter(double);
|
||||
void SaveIterates(bool);
|
||||
void SetLinearSolver(int);
|
||||
void SetLinearSolveTol(double);
|
||||
void FeasibilityRestoration(const BlockVector &, const Vector &, const Vector &, BlockVector &, double);
|
||||
virtual ~ParInteriorPointSolver();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,404 @@
|
||||
#include "mfem.hpp"
|
||||
#include "Problem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
|
||||
ParGeneralOptProblem::ParGeneralOptProblem() : block_offsetsx(3) {}
|
||||
|
||||
void ParGeneralOptProblem::Init(HYPRE_BigInt * dofOffsetsU_, HYPRE_BigInt * dofOffsetsM_)
|
||||
{
|
||||
dofOffsetsU = new HYPRE_BigInt[2];
|
||||
dofOffsetsM = new HYPRE_BigInt[2];
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
dofOffsetsU[i] = dofOffsetsU_[i];
|
||||
dofOffsetsM[i] = dofOffsetsM_[i];
|
||||
}
|
||||
dimU = dofOffsetsU[1] - dofOffsetsU[0];
|
||||
dimM = dofOffsetsM[1] - dofOffsetsM[0];
|
||||
dimC = dimM;
|
||||
|
||||
block_offsetsx[0] = 0;
|
||||
block_offsetsx[1] = dimU;
|
||||
block_offsetsx[2] = dimM;
|
||||
block_offsetsx.PartialSum();
|
||||
|
||||
MPI_Allreduce(&dimU, &dimUglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(&dimM, &dimMglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
|
||||
void ParGeneralOptProblem::CalcObjectiveGrad(const BlockVector &x, BlockVector &y) const
|
||||
{
|
||||
Duf(x, y.GetBlock(0));
|
||||
Dmf(x, y.GetBlock(1));
|
||||
}
|
||||
|
||||
ParGeneralOptProblem::~ParGeneralOptProblem()
|
||||
{
|
||||
block_offsetsx.DeleteAll();
|
||||
}
|
||||
|
||||
|
||||
// min E(d) s.t. g(d) >= 0
|
||||
// min_(d,s) E(d) s.t. c(d,s) := g(d) - s = 0, s >= 0
|
||||
ParOptProblem::ParOptProblem() : ParGeneralOptProblem()
|
||||
{
|
||||
}
|
||||
|
||||
void ParOptProblem::Init(HYPRE_BigInt * dofOffsetsU_, HYPRE_BigInt * dofOffsetsM_)
|
||||
{
|
||||
dofOffsetsU = new HYPRE_BigInt[2];
|
||||
dofOffsetsM = new HYPRE_BigInt[2];
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
dofOffsetsU[i] = dofOffsetsU_[i];
|
||||
dofOffsetsM[i] = dofOffsetsM_[i];
|
||||
}
|
||||
|
||||
dimU = dofOffsetsU[1] - dofOffsetsU[0];
|
||||
dimM = dofOffsetsM[1] - dofOffsetsM[0];
|
||||
dimC = dimM;
|
||||
|
||||
block_offsetsx[0] = 0;
|
||||
block_offsetsx[1] = dimU;
|
||||
block_offsetsx[2] = dimM;
|
||||
block_offsetsx.PartialSum();
|
||||
|
||||
MPI_Allreduce(&dimU, &dimUglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(&dimM, &dimMglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
ml.SetSize(dimM); ml = 0.0;
|
||||
Vector negIdentDiag(dimM);
|
||||
negIdentDiag = -1.0;
|
||||
Ih = GenerateHypreParMatrixFromDiagonal(dofOffsetsM, negIdentDiag);
|
||||
}
|
||||
|
||||
|
||||
double ParOptProblem::CalcObjective(const BlockVector &x) const { return E(x.GetBlock(0)); }
|
||||
|
||||
void ParOptProblem::Duf(const BlockVector &x, Vector &y) const { DdE(x.GetBlock(0), y); }
|
||||
|
||||
void ParOptProblem::Dmf(const BlockVector &x, Vector &y) const { y = 0.0; }
|
||||
|
||||
HypreParMatrix * ParOptProblem::Duuf(const BlockVector &x)
|
||||
{
|
||||
return DddE(x.GetBlock(0));
|
||||
}
|
||||
|
||||
HypreParMatrix * ParOptProblem::Dumf(const BlockVector &x) { return nullptr; }
|
||||
|
||||
HypreParMatrix * ParOptProblem::Dmuf(const BlockVector &x) { return nullptr; }
|
||||
|
||||
HypreParMatrix * ParOptProblem::Dmmf(const BlockVector &x) { return nullptr; }
|
||||
|
||||
void ParOptProblem::c(const BlockVector &x, Vector &y) const // c(u,m) = g(u) - m
|
||||
{
|
||||
g(x.GetBlock(0), y);
|
||||
y.Add(-1.0, x.GetBlock(1));
|
||||
}
|
||||
|
||||
HypreParMatrix * ParOptProblem::Duc(const BlockVector &x)
|
||||
{
|
||||
return Ddg(x.GetBlock(0));
|
||||
}
|
||||
|
||||
HypreParMatrix * ParOptProblem::Dmc(const BlockVector &x)
|
||||
{
|
||||
return Ih;
|
||||
}
|
||||
|
||||
ParOptProblem::~ParOptProblem()
|
||||
{
|
||||
delete[] dofOffsetsU;
|
||||
delete[] dofOffsetsM;
|
||||
delete Ih;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Obstacle Problem, no essential boundary conditions enforced
|
||||
// Hessian of energy term is K + M (stiffness + mass)
|
||||
ParObstacleProblem::ParObstacleProblem(ParFiniteElementSpace *Vh_,
|
||||
double (*fSource)(const Vector &),
|
||||
double (*obstacleSource)(const Vector &)) :
|
||||
ParOptProblem(), Vh(Vh_), J(nullptr)
|
||||
{
|
||||
Init(Vh->GetTrueDofOffsets(), Vh->GetTrueDofOffsets());
|
||||
cout << "dimU = " << dimU;
|
||||
f.SetSize(dimU); f = 0.0;
|
||||
psi.SetSize(dimU); psi = 0.0;
|
||||
|
||||
|
||||
Kform = new ParBilinearForm(Vh);
|
||||
Kform->AddDomainIntegrator(new MassIntegrator);
|
||||
Kform->AddDomainIntegrator(new DiffusionIntegrator);
|
||||
Kform->Assemble();
|
||||
Kform->Finalize();
|
||||
Kform->FormSystemMatrix(ess_tdof_list, K);
|
||||
|
||||
FunctionCoefficient fcoeff(fSource);
|
||||
fform = new ParLinearForm(Vh);
|
||||
fform->AddDomainIntegrator(new DomainLFIntegrator(fcoeff));
|
||||
fform->Assemble();
|
||||
Vector F(dimU);
|
||||
fform->ParallelAssemble(F);
|
||||
f.SetSize(dimU);
|
||||
f.Set(1.0, F);
|
||||
|
||||
Vector iDiag(dimU); iDiag = 1.0;
|
||||
SparseMatrix * Jacg = new SparseMatrix(iDiag);
|
||||
|
||||
J = new HypreParMatrix(MPI_COMM_WORLD, dimUglb, dofOffsetsU, Jacg);
|
||||
HypreStealOwnership(*J, *Jacg);
|
||||
delete Jacg;
|
||||
}
|
||||
|
||||
// Obstacle Problem, essential boundary conditions enforced
|
||||
// Hessian of energy term is K (stiffness)
|
||||
ParObstacleProblem::ParObstacleProblem(ParFiniteElementSpace *Vh_,
|
||||
double (*fSource)(const Vector &),
|
||||
double (*obstacleSource)(const Vector &),
|
||||
Array<int> tdof_list, Vector &xDC) : ParOptProblem(),
|
||||
Vh(Vh_), J(nullptr)
|
||||
{
|
||||
Init(Vh->GetTrueDofOffsets(), Vh->GetTrueDofOffsets());
|
||||
f.SetSize(dimU); f = 0.0;
|
||||
psi.SetSize(dimU); psi = 0.0;
|
||||
// elastic energy functional terms
|
||||
ess_tdof_list = tdof_list;
|
||||
Kform = new ParBilinearForm(Vh);
|
||||
Kform->AddDomainIntegrator(new DiffusionIntegrator);
|
||||
Kform->Assemble();
|
||||
Kform->Finalize();
|
||||
Kform->FormSystemMatrix(ess_tdof_list, K);
|
||||
|
||||
FunctionCoefficient fcoeff(fSource);
|
||||
fform = new ParLinearForm(Vh);
|
||||
fform->AddDomainIntegrator(new DomainLFIntegrator(fcoeff));
|
||||
fform->Assemble();
|
||||
Vector F(dimU);
|
||||
fform->ParallelAssemble(F);
|
||||
f.SetSize(dimU);
|
||||
f.Set(1.0, F);
|
||||
Kform->EliminateVDofsInRHS(ess_tdof_list, xDC, f);
|
||||
|
||||
// obstacle constraints --
|
||||
Vector iDiag(dimU); iDiag = 1.0;
|
||||
for(int i = 0; i < ess_tdof_list.Size(); i++)
|
||||
{
|
||||
iDiag(ess_tdof_list[i]) = 0.0;
|
||||
}
|
||||
SparseMatrix * Jacg = new SparseMatrix(iDiag);
|
||||
|
||||
J = new HypreParMatrix(MPI_COMM_WORLD, dimUglb, dofOffsetsU, Jacg);
|
||||
HypreStealOwnership(*J, *Jacg);
|
||||
delete Jacg;
|
||||
|
||||
FunctionCoefficient psi_fc(obstacleSource);
|
||||
ParGridFunction psi_gf(Vh);
|
||||
psi_gf.ProjectCoefficient(psi_fc);
|
||||
psi.Set(1.0, (*psi_gf.GetTrueDofs()));
|
||||
for(int i = 0; i < ess_tdof_list.Size(); i++)
|
||||
{
|
||||
psi(ess_tdof_list[i]) = xDC(ess_tdof_list[i]) - 1.e-8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
double ParObstacleProblem::E(const Vector &d) const
|
||||
{
|
||||
Vector Kd(K.Height()); Kd = 0.0;
|
||||
MFEM_VERIFY(d.Size() == K.Width(), "ParObstacleProblem::E - Inconsistent dimensions");
|
||||
K.Mult(d, Kd);
|
||||
return 0.5 * InnerProduct(MPI_COMM_WORLD, d, Kd) - InnerProduct(MPI_COMM_WORLD, f, d);
|
||||
}
|
||||
|
||||
void ParObstacleProblem::DdE(const Vector &d, Vector &gradE) const
|
||||
{
|
||||
gradE.SetSize(K.Height());
|
||||
MFEM_VERIFY(d.Size() == K.Width(), "ParObstacleProblem::DdE - Inconsistent dimensions");
|
||||
K.Mult(d, gradE);
|
||||
MFEM_VERIFY(f.Size() == K.Height(), "ParObstacleProblem::DdE - Inconsistent dimensions");
|
||||
gradE.Add(-1.0, f);
|
||||
}
|
||||
|
||||
HypreParMatrix * ParObstacleProblem::DddE(const Vector &d)
|
||||
{
|
||||
return &K;
|
||||
}
|
||||
|
||||
// g(d) = d >= \psi
|
||||
void ParObstacleProblem::g(const Vector &d, Vector &gd) const
|
||||
{
|
||||
MFEM_VERIFY(d.Size() == J->Width(), "ParObstacleProblem::g - Inconsistent dimensions");
|
||||
J->Mult(d, gd);
|
||||
MFEM_VERIFY(gd.Size() == J->Height(), "ParObstacleProblem::g - Inconsistent dimensions");
|
||||
gd.Add(-1.0, psi);
|
||||
}
|
||||
|
||||
HypreParMatrix * ParObstacleProblem::Ddg(const Vector &d)
|
||||
{
|
||||
return J;
|
||||
}
|
||||
|
||||
ParObstacleProblem::~ParObstacleProblem()
|
||||
{
|
||||
delete Kform;
|
||||
delete fform;
|
||||
delete J;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ReducedProblem::ReducedProblem(ParOptProblem * problem_, HYPRE_Int * constraintMask)
|
||||
{
|
||||
problem = problem_;
|
||||
J = nullptr;
|
||||
P = nullptr;
|
||||
|
||||
int nprocs = Mpi::WorldSize();
|
||||
int myrank = Mpi::WorldRank();
|
||||
|
||||
HYPRE_BigInt * dofOffsets = problem->GetDofOffsetsU();
|
||||
|
||||
// given a constraint mask, lets update the constraintOffsets
|
||||
// from the original problem
|
||||
int nLocConstraints = 0;
|
||||
int nProblemConstraints = problem->GetDimM();
|
||||
for (int i = 0; i < nProblemConstraints; i++)
|
||||
{
|
||||
if (constraintMask[i] == 1)
|
||||
{
|
||||
nLocConstraints += 1;
|
||||
}
|
||||
}
|
||||
|
||||
HYPRE_BigInt * constraintOffsets_reduced;
|
||||
constraintOffsets_reduced = offsetsFromLocalSizes(nLocConstraints);
|
||||
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
cout << "constraintOffsetsReduced_" << i << " = " << constraintOffsets_reduced[i] << ", (rank = " << myrank << ")\n";
|
||||
}
|
||||
|
||||
HYPRE_BigInt * constraintOffsets;
|
||||
constraintOffsets = offsetsFromLocalSizes(nProblemConstraints);
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
cout << "constraintOffsets_" << i << " = " << constraintOffsets[i] << ", (rank = " << myrank << ")\n";
|
||||
}
|
||||
|
||||
|
||||
P = GenerateProjector(constraintOffsets, constraintOffsets_reduced, constraintMask);
|
||||
|
||||
Init(dofOffsets, constraintOffsets_reduced);
|
||||
delete[] constraintOffsets_reduced;
|
||||
delete[] constraintOffsets;
|
||||
}
|
||||
|
||||
ReducedProblem::ReducedProblem(ParOptProblem * problem_, HypreParVector & constraintMask)
|
||||
{
|
||||
problem = problem_;
|
||||
J = nullptr;
|
||||
P = nullptr;
|
||||
|
||||
int nprocs = Mpi::WorldSize();
|
||||
int myrank = Mpi::WorldRank();
|
||||
|
||||
HYPRE_BigInt * dofOffsets = problem->GetDofOffsetsU();
|
||||
|
||||
// given a constraint mask, lets update the constraintOffsets
|
||||
// from the original problem
|
||||
int nLocConstraints = 0;
|
||||
int nProblemConstraints = problem->GetDimM();
|
||||
for (int i = 0; i < nProblemConstraints; i++)
|
||||
{
|
||||
if (constraintMask[i] == 1)
|
||||
{
|
||||
nLocConstraints += 1;
|
||||
}
|
||||
}
|
||||
cout << "nLocConstraints = " << nLocConstraints << ", (rank = " << myrank << ")\n";
|
||||
|
||||
HYPRE_BigInt * constraintOffsets_reduced;
|
||||
constraintOffsets_reduced = offsetsFromLocalSizes(nLocConstraints);
|
||||
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
cout << "constraintOffsetsReduced_" << i << " = " << constraintOffsets_reduced[i] << ", (rank = " << myrank << ")\n";
|
||||
}
|
||||
|
||||
HYPRE_BigInt * constraintOffsets;
|
||||
constraintOffsets = offsetsFromLocalSizes(nProblemConstraints);
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
cout << "constraintOffsets_" << i << " = " << constraintOffsets[i] << ", (rank = " << myrank << ")\n";
|
||||
}
|
||||
|
||||
|
||||
P = GenerateProjector(constraintOffsets, constraintOffsets_reduced, constraintMask);
|
||||
|
||||
Init(dofOffsets, constraintOffsets_reduced);
|
||||
delete[] constraintOffsets_reduced;
|
||||
delete[] constraintOffsets;
|
||||
}
|
||||
|
||||
// energy objective E(d)
|
||||
double ReducedProblem::E(const Vector &d) const
|
||||
{
|
||||
return problem->E(d);
|
||||
}
|
||||
|
||||
|
||||
// gradient of energy objective
|
||||
void ReducedProblem::DdE(const Vector &d, Vector & gradE) const
|
||||
{
|
||||
problem->DdE(d, gradE);
|
||||
}
|
||||
|
||||
|
||||
HypreParMatrix * ReducedProblem::DddE(const Vector &d)
|
||||
{
|
||||
return problem->DddE(d);
|
||||
}
|
||||
|
||||
void ReducedProblem::g(const Vector &d, Vector &gd) const
|
||||
{
|
||||
Vector gdfull(problem->GetDimM()); gdfull = 0.0;
|
||||
problem->g(d, gdfull);
|
||||
P->Mult(gdfull, gd);
|
||||
}
|
||||
|
||||
|
||||
HypreParMatrix * ReducedProblem::Ddg(const Vector &d)
|
||||
{
|
||||
HypreParMatrix * Jfull = problem->Ddg(d);
|
||||
if (J != nullptr)
|
||||
{
|
||||
delete J; J = nullptr;
|
||||
}
|
||||
J = ParMult(P, Jfull, true);
|
||||
return J;
|
||||
}
|
||||
|
||||
ReducedProblem::~ReducedProblem()
|
||||
{
|
||||
delete P;
|
||||
if (J != nullptr)
|
||||
{
|
||||
delete J;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "utilities.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
#ifndef PARPROBLEM_DEFS
|
||||
#define PARPROBLEM_DEFS
|
||||
|
||||
// abstract ParGeneralOptProblem class
|
||||
// of the form
|
||||
// min_(u,m) f(u,m) s.t. c(u,m)=0 and m>=ml
|
||||
// the primal variable (u, m) is represented as a BlockVector
|
||||
class ParGeneralOptProblem
|
||||
{
|
||||
protected:
|
||||
int dimU, dimM, dimC;
|
||||
int dimUglb, dimMglb;
|
||||
HYPRE_BigInt * dofOffsetsU;
|
||||
HYPRE_BigInt * dofOffsetsM;
|
||||
Array<int> block_offsetsx;
|
||||
Vector ml;
|
||||
public:
|
||||
ParGeneralOptProblem();
|
||||
virtual void Init(HYPRE_BigInt * dofOffsetsU_, HYPRE_BigInt * dofOffsetsM_);
|
||||
virtual double CalcObjective(const BlockVector &) const = 0;
|
||||
virtual void Duf(const BlockVector &, Vector &) const = 0;
|
||||
virtual void Dmf(const BlockVector &, Vector &) const = 0;
|
||||
void CalcObjectiveGrad(const BlockVector &, BlockVector &) const;
|
||||
virtual HypreParMatrix * Duuf(const BlockVector &) = 0;
|
||||
virtual HypreParMatrix * Dumf(const BlockVector &) = 0;
|
||||
virtual HypreParMatrix * Dmuf(const BlockVector &) = 0;
|
||||
virtual HypreParMatrix * Dmmf(const BlockVector &) = 0;
|
||||
virtual HypreParMatrix * Duc(const BlockVector &) = 0;
|
||||
virtual HypreParMatrix * Dmc(const BlockVector &) = 0;
|
||||
virtual void c(const BlockVector &, Vector &) const = 0;
|
||||
int GetDimU() const { return dimU; };
|
||||
int GetDimM() const { return dimM; };
|
||||
int GetDimC() const { return dimC; };
|
||||
int GetDimUGlb() const { return dimUglb; };
|
||||
int GetDimMGlb() const { return dimMglb; };
|
||||
HYPRE_BigInt * GetDofOffsetsU() const { return dofOffsetsU; };
|
||||
HYPRE_BigInt * GetDofOffsetsM() const { return dofOffsetsM; };
|
||||
Vector Getml() const { return ml; };
|
||||
~ParGeneralOptProblem();
|
||||
};
|
||||
|
||||
|
||||
// abstract ContactProblem class
|
||||
// of the form
|
||||
// min_d e(d) s.t. g(d) >= 0
|
||||
class ParOptProblem : public ParGeneralOptProblem
|
||||
{
|
||||
protected:
|
||||
HypreParMatrix * Ih;
|
||||
public:
|
||||
ParOptProblem();
|
||||
void Init(HYPRE_BigInt *, HYPRE_BigInt *);
|
||||
|
||||
// ParGeneralOptProblem methods are defined in terms of
|
||||
// ParOptProblem specific methods: E, DdE, DddE, g, Ddg
|
||||
double CalcObjective(const BlockVector &) const;
|
||||
void Duf(const BlockVector &, Vector &) const;
|
||||
void Dmf(const BlockVector &, Vector &) const;
|
||||
HypreParMatrix * Duuf(const BlockVector &);
|
||||
HypreParMatrix * Dumf(const BlockVector &);
|
||||
HypreParMatrix * Dmuf(const BlockVector &);
|
||||
HypreParMatrix * Dmmf(const BlockVector &);
|
||||
void c(const BlockVector &, Vector &) const;
|
||||
HypreParMatrix * Duc(const BlockVector &);
|
||||
HypreParMatrix * Dmc(const BlockVector &);
|
||||
|
||||
// ParOptProblem specific methods:
|
||||
|
||||
// energy objective function e(d)
|
||||
// input: d an mfem::Vector
|
||||
// output: e(d) a double
|
||||
virtual double E(const Vector &d) const = 0;
|
||||
|
||||
// gradient of energy objective De / Dd
|
||||
// input: d an mfem::Vector,
|
||||
// gradE an mfem::Vector, which will be the gradient of E at d
|
||||
// output: none
|
||||
virtual void DdE(const Vector &d, Vector &gradE) const = 0;
|
||||
|
||||
// Hessian of energy objective D^2 e / Dd^2
|
||||
// input: d, an mfem::Vector
|
||||
// output: The Hessian of the energy objective at d, a pointer to a HypreParMatrix
|
||||
virtual HypreParMatrix * DddE(const Vector &d) = 0;
|
||||
|
||||
// Constraint function g(d) >= 0, e.g., gap function
|
||||
// input: d, an mfem::Vector,
|
||||
// gd, an mfem::Vector, which upon successfully calling the g method will be
|
||||
// the evaluation of the function g at d
|
||||
// output: none
|
||||
virtual void g(const Vector &d, Vector &gd) const = 0;
|
||||
|
||||
// Jacobian of constraint function Dg / Dd, e.g., gap function Jacobian
|
||||
// input: d, an mfem::Vector,
|
||||
// output: The Jacobain of the constraint function g at d, a pointer to a HypreParMatrix
|
||||
virtual HypreParMatrix * Ddg(const Vector &) = 0;
|
||||
virtual ~ParOptProblem();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ParObstacleProblem : public ParOptProblem
|
||||
{
|
||||
protected:
|
||||
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d >= \psi
|
||||
// stiffness matrix used to define objective
|
||||
ParBilinearForm *Kform;
|
||||
ParLinearForm *fform;
|
||||
Array<int> ess_tdof_list; // needed for calls to FormSystemMatrix
|
||||
HypreParMatrix K;
|
||||
HypreParMatrix *J;
|
||||
ParFiniteElementSpace *Vh;
|
||||
Vector f;
|
||||
Vector psi;
|
||||
public :
|
||||
ParObstacleProblem(ParFiniteElementSpace*, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &));
|
||||
ParObstacleProblem(ParFiniteElementSpace*, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &), Array<int> tdof_list, Vector &);
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
HypreParMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
HypreParMatrix* Ddg(const Vector &);
|
||||
virtual ~ParObstacleProblem();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ReducedProblem : public ParOptProblem
|
||||
{
|
||||
protected:
|
||||
HypreParMatrix *J;
|
||||
HypreParMatrix *P; // projector
|
||||
ParOptProblem *problem;
|
||||
public:
|
||||
ReducedProblem(ParOptProblem *problem, HYPRE_Int * constraintMask);
|
||||
ReducedProblem(ParOptProblem *problem, HypreParVector & constraintMask);
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
HypreParMatrix * DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
HypreParMatrix * Ddg(const Vector &);
|
||||
virtual ~ReducedProblem();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,181 @@
|
||||
// Example Problem 1
|
||||
//
|
||||
//
|
||||
// Compile with: make ParTestProblem1
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ./ParTestProblem1
|
||||
//
|
||||
//
|
||||
// Description: This example code demonstrates the use of the MFEM based
|
||||
// interior-point solver to solve the
|
||||
// bound-constrained minimization problem
|
||||
//
|
||||
// minimize_(x \in R^n) 1/2 x^T x subject to x - xl ≥ 0 (component-wise).
|
||||
//
|
||||
#include "mfem.hpp"
|
||||
#include "Problem.hpp"
|
||||
#include "IPsolver.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
|
||||
|
||||
// solve min 1/2 x^T K x s.t. J x - xl >= 0
|
||||
// where K and J are identity matrices and xl
|
||||
// has uniform random values in [-1, 1]
|
||||
// for the Lagrangian L(x, s, l, z) = 1/2 x^T x + l^T (x - xl - s) - z^T s
|
||||
// the optimal solution is x*_i = max{0, (xl)_i}, z*_i = x*_i
|
||||
|
||||
|
||||
class ParEx1Problem : public ParOptProblem
|
||||
{
|
||||
protected:
|
||||
HypreParMatrix *K;
|
||||
HypreParMatrix *J;
|
||||
Vector xl;
|
||||
//HYPRE_BigInt * dofOffsets;
|
||||
public:
|
||||
// create offsets internally only pass problem size
|
||||
//ParEx1Problem(HYPRE_BigInt * offsets);
|
||||
ParEx1Problem(int n);
|
||||
double E(const Vector &) const;
|
||||
void DdE(const Vector &, Vector &) const;
|
||||
HypreParMatrix* DddE(const Vector &);
|
||||
void g(const Vector &, Vector &) const;
|
||||
HypreParMatrix* Ddg(const Vector &);
|
||||
virtual ~ParEx1Problem();
|
||||
};
|
||||
|
||||
|
||||
|
||||
void mfemIPSolve(ParGeneralOptProblem & problem, Vector &x, Vector &lambda)
|
||||
{
|
||||
ParInteriorPointSolver IPoptimizer(&problem);
|
||||
|
||||
int dimX = problem.GetDimU();
|
||||
Vector x0(dimX); x0 = 100.0;
|
||||
x.SetSize(dimX); x = 0.0;
|
||||
|
||||
double OptTol = 1.e-6;
|
||||
double LinSolveTol = 1.e-10;
|
||||
int linSolveStrategy = 2;
|
||||
int MaxOptIter = 30;
|
||||
IPoptimizer.SetTol(OptTol);
|
||||
IPoptimizer.SetLinearSolveTol(LinSolveTol);
|
||||
IPoptimizer.SetLinearSolver(linSolveStrategy);
|
||||
IPoptimizer.SetMaxIter(MaxOptIter);
|
||||
IPoptimizer.Mult(x0, x);
|
||||
|
||||
int dimM = problem.GetDimM();
|
||||
lambda.SetSize(dimM);
|
||||
IPoptimizer.GetLagrangeMultiplier(lambda);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Initialize MPI
|
||||
Mpi::Init();
|
||||
Hypre::Init();
|
||||
|
||||
int n = 10;
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&n, "-n", "--n", \
|
||||
"Size of the optimization problem (dimension of primal variable)");
|
||||
args.ParseCheck();
|
||||
|
||||
|
||||
ParEx1Problem problem(n);
|
||||
|
||||
Vector xOptimal, lambdaOptimal;
|
||||
mfemIPSolve(problem, xOptimal, lambdaOptimal);
|
||||
for(int i = 0; i < xOptimal.Size(); i++)
|
||||
{
|
||||
cout << "optimal (x, z)_" << i << " = (" << xOptimal(i) << ", " << lambdaOptimal(i) << ")\n";
|
||||
}
|
||||
|
||||
Mpi::Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Ex1Problem
|
||||
// min 1/2 x^T K x such that J x - xl >= 0
|
||||
// where K and J are identity matrices
|
||||
ParEx1Problem::ParEx1Problem(int n) : ParOptProblem(), K(nullptr), J(nullptr)
|
||||
{
|
||||
// generate the parallel partition of the
|
||||
// variable x and the
|
||||
int nprocs = Mpi::WorldSize();
|
||||
int myrank = Mpi::WorldRank();
|
||||
|
||||
|
||||
HYPRE_BigInt * dofOffsets = new HYPRE_BigInt[2];
|
||||
dofOffsets[0] = HYPRE_BigInt(myrank * n / nprocs);
|
||||
dofOffsets[1] = HYPRE_BigInt((myrank + 1) * n / nprocs);
|
||||
|
||||
Init(dofOffsets, dofOffsets);
|
||||
|
||||
Vector iDiag(dofOffsets[1] - dofOffsets[0]); iDiag = 1.0;
|
||||
|
||||
K = GenerateHypreParMatrixFromDiagonal(dofOffsets, iDiag);
|
||||
|
||||
J = GenerateHypreParMatrixFromDiagonal(dofOffsets, iDiag);
|
||||
|
||||
xl.SetSize(dofOffsets[1] - dofOffsets[0]);
|
||||
xl.Randomize(myrank);
|
||||
xl *= 2.0;
|
||||
xl -= 1.0;
|
||||
delete[] dofOffsets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double ParEx1Problem::E(const Vector & x) const
|
||||
{
|
||||
Vector Kx(K->Height()); Kx = 0.0;
|
||||
MFEM_VERIFY(x.Size() == K->Width(), "ParEx1Problem::E - Inconsistent dimensions");
|
||||
K->Mult(x, Kx);
|
||||
return 0.5 * InnerProduct(MPI_COMM_WORLD, x, Kx);
|
||||
}
|
||||
|
||||
void ParEx1Problem::DdE(const Vector &x, Vector &gradE) const
|
||||
{
|
||||
gradE.SetSize(K->Height());
|
||||
MFEM_VERIFY(x.Size() == K->Width(), "ParEx1Problem::DdE - Inconsistent dimensions");
|
||||
K->Mult(x, gradE);
|
||||
}
|
||||
|
||||
HypreParMatrix * ParEx1Problem::DddE(const Vector &x)
|
||||
{
|
||||
return K;
|
||||
}
|
||||
|
||||
// g(x) = x - xl >= 0
|
||||
void ParEx1Problem::g(const Vector &x, Vector &gx) const
|
||||
{
|
||||
MFEM_VERIFY(x.Size() == J->Width(), "ParEx1Problem::g - Inconsistent dimensions");
|
||||
J->Mult(x, gx);
|
||||
MFEM_VERIFY(gx.Size() == J->Height(), "ParEx1Problem::g - Inconsistent dimensions");
|
||||
gx.Add(-1.0, xl);
|
||||
}
|
||||
|
||||
HypreParMatrix * ParEx1Problem::Ddg(const Vector &)
|
||||
{
|
||||
return J;
|
||||
}
|
||||
|
||||
ParEx1Problem::~ParEx1Problem()
|
||||
{
|
||||
delete K;
|
||||
delete J;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
// Spherical Obstacle Problem
|
||||
//
|
||||
//
|
||||
// Compile with: make ParSphericalObstacleProblem
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ./ParSphericalObstacleProblem -linSolver 0
|
||||
// mpirun -np 4 ./ParSphericalObstacleProblem -linSolver 1
|
||||
// mpirun -np 4 ./ParSphericalObstacleProblem -linSolver 2
|
||||
//
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve the
|
||||
// bound-constrained energy minimization problem
|
||||
//
|
||||
// minimize ||∇u||² subject to u ≥ ϕ in H¹₀.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "Problem.hpp"
|
||||
#include "IPsolver.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
double fRhs(const Vector &);
|
||||
double spherical_obstacle(const Vector &);
|
||||
double exact_solution_obstacle(const Vector &);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Initialize MPI
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
int FEorder = 1; // order of the finite elements
|
||||
int linSolver = 2;
|
||||
int maxIPMiters = 30;
|
||||
int ref_levels = 3;
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&FEorder, "-o", "--order",\
|
||||
"Order of the finite elements.");
|
||||
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
|
||||
"IP-Newton linear system solution strategy.");
|
||||
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
|
||||
"Maximum number of IPM iterations");
|
||||
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
|
||||
"Mesh Refinement");
|
||||
|
||||
args.ParseCheck();
|
||||
|
||||
const char *meshFile = "disk.mesh";
|
||||
Mesh mesh(meshFile, 1, 1);
|
||||
int dim = mesh.Dimension(); // geometric dimension of the meshed domain
|
||||
{
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
|
||||
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
|
||||
ParFiniteElementSpace *Vh = new ParFiniteElementSpace(&pmesh, fec);
|
||||
Array<int> boundary_dofs;
|
||||
Vh->GetBoundaryTrueDofs(boundary_dofs);
|
||||
int dimD = Vh->GetTrueVSize();
|
||||
Vector xDC(dimD); xDC = 0.0;
|
||||
|
||||
ParObstacleProblem problem(Vh, &fRhs, &spherical_obstacle, boundary_dofs, xDC);
|
||||
Vector x0(dimD); x0.Set(1.0, xDC);
|
||||
Vector xf(dimD); xf = 0.0;
|
||||
|
||||
ParInteriorPointSolver optimizer(&problem);
|
||||
optimizer.SetTol(1.e-7);
|
||||
optimizer.SetLinearSolveTol(1.e-9);
|
||||
optimizer.SetLinearSolver(linSolver);
|
||||
optimizer.SetMaxIter(maxIPMiters);
|
||||
optimizer.Mult(x0, xf);
|
||||
|
||||
ParGridFunction d_gf(Vh);
|
||||
|
||||
d_gf.SetFromTrueDofs(xf);
|
||||
|
||||
|
||||
FunctionCoefficient dtrue_fc(exact_solution_obstacle); // analytic solution
|
||||
ParGridFunction dtrue_gf(Vh);
|
||||
dtrue_gf.ProjectCoefficient(dtrue_fc);
|
||||
|
||||
double L2error = d_gf.ComputeL2Error(dtrue_fc);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| u_h - u ||_{L^2} = " << L2error << '\n' << endl;
|
||||
}
|
||||
|
||||
ParaViewDataCollection paraview_dc("SphericalObstacleProblem", &pmesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(FEorder);
|
||||
paraview_dc.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc.SetHighOrderOutput(true);
|
||||
paraview_dc.SetCycle(0);
|
||||
paraview_dc.SetTime(0.0);
|
||||
paraview_dc.RegisterField("u(x,y) (analytic)", &dtrue_gf);
|
||||
paraview_dc.RegisterField("u(x,y) (numerical)", &d_gf);
|
||||
paraview_dc.Save();
|
||||
|
||||
delete Vh;
|
||||
delete fec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double fRhs(const Vector &x)
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
|
||||
|
||||
double spherical_obstacle(const Vector &pt)
|
||||
{
|
||||
double x = pt(0), y = pt(1);
|
||||
double r = sqrt(x*x + y*y);
|
||||
double r0 = 0.5;
|
||||
double beta = 0.9;
|
||||
|
||||
double b = r0*beta;
|
||||
double tmp = sqrt(r0*r0 - b*b);
|
||||
double B = tmp + b*b/tmp;
|
||||
double C = -b/tmp;
|
||||
|
||||
if (r > b)
|
||||
{
|
||||
return B + r * C;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sqrt(r0*r0 - r*r);
|
||||
}
|
||||
}
|
||||
|
||||
double exact_solution_obstacle(const Vector &pt)
|
||||
{
|
||||
double x = pt(0), y = pt(1);
|
||||
double r = sqrt(x*x + y*y);
|
||||
double r0 = 0.5;
|
||||
double a = 0.348982574111686;
|
||||
double A = -0.340129705945858;
|
||||
|
||||
if (r > a)
|
||||
{
|
||||
return A * log(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
return sqrt(r0*r0-r*r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
5
|
||||
1 3 4 5 6 7
|
||||
1 3 0 1 5 4
|
||||
1 3 1 2 6 5
|
||||
1 3 3 7 6 2
|
||||
1 3 0 4 7 3
|
||||
|
||||
boundary
|
||||
4
|
||||
1 1 0 1
|
||||
1 1 2 3
|
||||
1 1 1 2
|
||||
1 1 3 0
|
||||
|
||||
edges
|
||||
12
|
||||
0 0 1
|
||||
0 4 5
|
||||
0 7 6
|
||||
0 3 2
|
||||
1 1 2
|
||||
1 5 6
|
||||
1 4 7
|
||||
1 0 3
|
||||
2 0 4
|
||||
2 1 5
|
||||
2 2 6
|
||||
2 3 7
|
||||
|
||||
vertices
|
||||
8
|
||||
|
||||
knotvectors
|
||||
3
|
||||
2 3 0 0 0 1 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
0.70710678118655
|
||||
1
|
||||
1
|
||||
0.70710678118655
|
||||
0.70710678118655
|
||||
1
|
||||
1
|
||||
0.70710678118655
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
0.85355339059327
|
||||
0.85355339059327
|
||||
0.85355339059327
|
||||
0.85355339059327
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS2
|
||||
VDim: 2
|
||||
Ordering: 1
|
||||
|
||||
-0.70710678118 -0.70710678118
|
||||
0.70710678118 -0.70710678118
|
||||
0.70710678118 0.70710678118
|
||||
-0.70710678118 0.70710678118
|
||||
-0.35355339059 -0.35355339059
|
||||
0.35355339059 -0.35355339059
|
||||
0.35355339059 0.35355339059
|
||||
-0.35355339059 0.35355339059
|
||||
0 -1.41421356236
|
||||
0 -0.35355339059
|
||||
0 0.35355339059
|
||||
0 1.41421356236
|
||||
1.41421356236 0
|
||||
0.35355339059 0
|
||||
-0.35355339059 0
|
||||
-1.41421356236 0
|
||||
-0.530330085885 -0.530330085885
|
||||
0.530330085885 -0.530330085885
|
||||
0.530330085885 0.530330085885
|
||||
-0.530330085885 0.530330085885
|
||||
0 0
|
||||
0 -0.883883476475
|
||||
0.883883476475 0
|
||||
0 0.883883476475
|
||||
-0.883883476475 0
|
||||
@@ -0,0 +1,7 @@
|
||||
MFEM INLINE mesh v1.0
|
||||
|
||||
type = quad
|
||||
nx = 4
|
||||
ny = 4
|
||||
sx = 1.0
|
||||
sy = 1.0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user