Compare commits

..
111 changed files with 1349 additions and 12002 deletions
-1
View File
@@ -215,7 +215,6 @@ miniapps/meshing/polar-nc.mesh
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
miniapps/navier/navier_kovasznay_vs
miniapps/navier/navier_tgv
miniapps/navier/navier_shear
miniapps/navier/navier_3dfoc
-36
View File
@@ -11,12 +11,6 @@
Version 4.2.1 (development)
===========================
- Added high-order matrix-free auxiliary Maxwell solver for H(curl) problems,
as described in Barker and Kolev 2020 (https://doi.org/10.1002/nla.2348).
- Added matrix-free GPU-enabled implementations of GradientInterpolator and
IdentityInterpolator.
- Added interface to MUMPS direct solver. Its usage is demonstrated in ex25p.
See http://mumps.enseeiht.fr/ for more details. Supported versions >= 5.1.1.
@@ -26,41 +20,11 @@ Version 4.2.1 (development)
- Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to
help identify elements with poor mesh quality.
- Added support for the "BR2" discontinuous Galerkin discretization for
diffusion via DGDiffusionBR2Integrator (see Example 14/14p).
- Generalized the Multigrid class to support non-geometric multigrid. The
previous functionality, based on FiniteElementSpaceHierarchy, is now available
in the derived class GeometricMultigrid.
- Upgraded the Catch unit test framework from version 2.13.0 to version 2.13.2.
- Implemented a filter method for the Navier miniapp to stabilize highly
turbulent flows in direct numerical simulation.
- Added partial assembly and device support to Example 25/25p, with diagonal
preconditioning.
- Implemented a variable step-size IMEX (VSSIMEX) method for the Navier miniapp.
- Added new mesh quality metrics and improved the untangling capabilities of the
TMOP-based mesh optimization algorithms.
- Changed the interface for the error estimator.
- Implemented the parallel Kelly error indicator for scalar-valued problems.
- Added new classes DenseSymmetricMatrix and SymmetricMatrixCoefficient for
efficient evaluation of symmetric matrix coefficients. This replaces the now
deprecated EvalSymmetric in MatrixCoefficient. Added DiagonalMatrixCoefficient
for clarity, which is a typedef of VectorCoefficient.
- Implemented an adaptive linear solver tolerance option for NewtonSolver based
on the algorithm of Eisenstat and Walker.
- Extending support for L2 basis functions using MapTypes VALUE and INTEGRAL in
linear interpolators and GridFunction "GetValue" methods.
Version 4.2, released on October 30, 2020
=========================================
+1 -2
View File
@@ -9,8 +9,7 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# The variable CMAKE_CXX_STANDARD and related were introduced in CMake v3.1
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 2.8.11)
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
"Path to optional user configuration file.")
+2 -2
View File
@@ -710,10 +710,10 @@ The specific libraries and their options are:
Versions: libCEED >= 0.7.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
Versions: RAJA >= 0.13.0.
Versions: RAJA >= 0.10.0.
- Umpire, used when MFEM_USE_UMPIRE = YES.
URL: https://github.com/LLNL/Umpire
+7 -7
View File
@@ -25,16 +25,16 @@ mfem_find_package(METIS METIS METIS_DIR "include;Lib" "metis.h"
int main()
{
idx_t n = 10;
idx_t nparts = 5;
idx_t edgecut;
idx_t* partitioning = new idx_t[10];
idx_t* I = partitioning,
int n = 10;
int nparts = 5;
int edgecut;
int* partitioning = new int[10];
int* I = partitioning,
* J = partitioning;
idx_t ncon = 1;
int ncon = 1;
int err;
idx_t options[40];
int options[40];
METIS_SetDefaultOptions(options);
options[10] = 1; // set METIS_OPTION_CONTIG
@@ -754,13 +754,7 @@ function(mfem_export_mk_files)
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${MFEM_CXX})
set(MFEM_CPPFLAGS "")
get_target_property(cxx_std mfem CXX_STANDARD)
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
# property is set, then we need to use a variable like:
# CMAKE_CXX11_EXTENSION_COMPILE_OPTION
set(cxx_std_flag ${CMAKE_CXX${cxx_std}_STANDARD_COMPILE_OPTION})
string(STRIP
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
MFEM_CXXFLAGS)
set(MFEM_TPLFLAGS "")
foreach(dir ${MFEM_TPL_INCLUDE_DIRS})
+23 -27
View File
@@ -161,7 +161,7 @@ endif
ZLIB_DIR =
ZLIB_OPT = $(if $(ZLIB_DIR),-I$(ZLIB_DIR)/include)
ZLIB_LIB = $(if $(ZLIB_DIR),$(ZLIB_RPATH) -L$(ZLIB_DIR)/lib ,)-lz
ZLIB_RPATH = $(XLINKER)-rpath,$(ZLIB_DIR)/lib
ZLIB_RPATH = -Wl,-rpath,$(ZLIB_DIR)/lib
LIBUNWIND_OPT = -g
LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
@@ -231,21 +231,19 @@ MESQUITE_LIB = -L$(MESQUITE_DIR)/lib -lmesquite
LIB_RT = $(if $(NOTMAC),-lrt,)
SUITESPARSE_DIR = @MFEM_DIR@/../SuiteSparse
SUITESPARSE_OPT = -I$(SUITESPARSE_DIR)/include
SUITESPARSE_LIB = $(XLINKER)-rpath,$(SUITESPARSE_DIR)/lib\
-L$(SUITESPARSE_DIR)/lib -lklu -lbtf -lumfpack -lcholmod -lcolamd -lamd -lcamd\
-lccolamd -lsuitesparseconfig $(LIB_RT) $(METIS_LIB) $(LAPACK_LIB)
SUITESPARSE_LIB = -Wl,-rpath,$(SUITESPARSE_DIR)/lib -L$(SUITESPARSE_DIR)/lib\
-lklu -lbtf -lumfpack -lcholmod -lcolamd -lamd -lcamd -lccolamd\
-lsuitesparseconfig $(LIB_RT) $(METIS_LIB) $(LAPACK_LIB)
# SuperLU library configuration
ifeq ($(MFEM_USE_SUPERLU5),YES)
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_5.1.0
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib\
-lsuperlu_dist_5.1.0
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib -lsuperlu_dist_5.1.0
else
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_6.3.1
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64\
-lsuperlu_dist -lblas
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64 -lsuperlu_dist -lblas
endif
# SCOTCH library configuration (required by STRUMPACK <= v2.1.0, optional in
@@ -271,7 +269,7 @@ MPI_FORTRAN_LIB = -lmpifort
# MUMPS library configuration
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.2.0
MUMPS_OPT = -I$(MUMPS_DIR)/include
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
MUMPS_LIB = -Wl,-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
-lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
# STRUMPACK library configuration
@@ -301,8 +299,8 @@ GNUTLS_LIB = -lgnutls
NETCDF_DIR = $(HOME)/local
HDF5_DIR = $(HOME)/local
NETCDF_OPT = -I$(NETCDF_DIR)/include -I$(HDF5_DIR)/include $(ZLIB_OPT)
NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
NETCDF_LIB = -Wl,-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
-lnetcdf -lhdf5_hl -lhdf5 $(ZLIB_LIB)
# PETSc library configuration (version greater or equal to 3.8 or the dev branch)
@@ -314,10 +312,9 @@ PETSC_INC_VAR = PETSC_CC_INCLUDES
PETSC_LIB_VAR = PETSC_EXTERNAL_LIB_BASIC
ifeq ($(PETSC_FOUND),YES)
PETSC_OPT := $(shell sed -n "s/$(PETSC_INC_VAR) = *//p" $(PETSC_VARS))
PETSC_DEP := $(shell sed -n "s/$(PETSC_LIB_VAR) = *//p" $(PETSC_VARS))
PETSC_LIB = $(XLINKER)-rpath,$(abspath $(PETSC_DIR))/lib\
-L$(abspath $(PETSC_DIR))/lib -lpetsc\
$(subst $(CXX_XLINKER),$(XLINKER),$(PETSC_DEP))
PETSC_LIB := $(shell sed -n "s/$(PETSC_LIB_VAR) = *//p" $(PETSC_VARS))
PETSC_LIB := -Wl,-rpath,$(abspath $(PETSC_DIR))/lib\
-L$(abspath $(PETSC_DIR))/lib -lpetsc $(PETSC_LIB)
endif
SLEPC_DIR := $(MFEM_DIR)/../slepc
@@ -329,10 +326,9 @@ ifeq ($(SLEPC_FOUND),YES)
SLEPC_OPT := $(shell sed -n "s/$(SLEPC_INC_VAR) *= *//p" $(SLEPC_VARS))
# Some additional external libraries might be defined in this file
-include ${SLEPC_DIR}/${PETSC_ARCH}/lib/slepc/conf/slepcvariables
SLEPC_DEP := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
SLEPC_LIB = $(XLINKER)-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc\
$(subst $(CXX_XLINKER),$(XLINKER),$(SLEPC_DEP))
SLEPC_LIB := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
SLEPC_LIB := -Wl,-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc $(SLEPC_LIB)
endif
# MPFR library configuration
@@ -343,7 +339,7 @@ MPFR_LIB = -lmpfr
CONDUIT_DIR = @MFEM_DIR@/../conduit
CONDUIT_OPT = -I$(CONDUIT_DIR)/include/conduit
CONDUIT_LIB = \
$(XLINKER)-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-lconduit -lconduit_relay -lconduit_blueprint -ldl
# Check if Conduit was built with hdf5 support, by looking
@@ -351,7 +347,7 @@ CONDUIT_LIB = \
CONDUIT_HDF5_HEADER=$(CONDUIT_DIR)/include/conduit/conduit_relay_hdf5.hpp
ifneq (,$(wildcard $(CONDUIT_HDF5_HEADER)))
CONDUIT_OPT += -I$(HDF5_DIR)/include
CONDUIT_LIB += $(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
CONDUIT_LIB += -Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-lhdf5 $(ZLIB_LIB)
endif
@@ -361,9 +357,9 @@ SIDRE_DIR = @MFEM_DIR@/../axom
SIDRE_OPT = -I$(SIDRE_DIR)/include -I$(CONDUIT_DIR)/include/conduit\
-I$(HDF5_DIR)/include
SIDRE_LIB = \
$(XLINKER)-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
$(XLINKER)-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-Wl,-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-laxom -lconduit -lconduit_relay -lconduit_blueprint -lhdf5 $(ZLIB_LIB) -ldl
# PUMI
@@ -419,9 +415,9 @@ MKL_CPARDISO_DIR ?=
MKL_MPI_WRAPPER ?= mkl_blacs_mpich_lp64
MKL_LIBRARY_SUBDIR ?= lib
MKL_CPARDISO_OPT = -I$(MKL_CPARDISO_DIR)/include
MKL_CPARDISO_LIB = $(XLINKER)-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
MKL_CPARDISO_LIB = -Wl,-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
# If YES, enable some informational messages
VERBOSE = NO
+8 -8
View File
@@ -31,14 +31,14 @@ POINTS 27 double
7 0.5 1
8 0.5 1
CELLS 8 56
6 0 18 9 1 19 10
6 1 19 10 2 20 11
6 2 20 11 3 21 12
6 3 21 12 4 22 13
6 4 22 13 5 23 14
6 5 23 14 6 24 15
6 6 24 15 7 25 16
6 7 25 16 8 26 17
6 0 9 18 1 10 19
6 1 10 19 2 11 20
6 2 11 20 3 12 21
6 3 12 21 4 13 22
6 4 13 22 5 14 23
6 5 14 23 6 15 24
6 6 15 24 7 16 25
6 7 16 25 8 17 26
CELL_TYPES 8
13
13
-4
View File
@@ -205,14 +205,10 @@ int main(int argc, char *argv[])
if (amgx_solver)
{
amgx.SetConvergenceCheck(true);
amgx.Mult(B,X);
}
else
{
// Omit convergence check at the AmgX level when using as a
// preconditioner.
amgx.SetConvergenceCheck(false);
PCG(*A.As<SparseMatrix>(), amgx, B, X, 3, 40, 1e-12, 0.0);
}
}
-1
View File
@@ -264,7 +264,6 @@ int main(int argc, char *argv[])
}
amgx.SetOperator(*A.As<HypreParMatrix>());
amgx.SetConvergenceCheck(true);
amgx.Mult(B, X);
// Release MPI communicators and resources created by AmgX
-1
View File
@@ -30,7 +30,6 @@
// Device sample runs:
// ex1 -pa -d cuda
// ex1 -pa -d raja-cuda
// * ex1 -pa -d raja-hip
// ex1 -pa -d occa-cuda
// ex1 -pa -d raja-omp
// ex1 -pa -d occa-omp
-5
View File
@@ -178,7 +178,6 @@ int main(int argc, char *argv[])
double visc = 1e-2;
double mu = 0.25;
double K = 5.0;
bool adaptive_lin_rtol = true;
bool visualization = true;
int vis_steps = 1;
@@ -207,9 +206,6 @@ int main(int argc, char *argv[])
"Shear modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&K, "-K", "--bulk-modulus",
"Bulk modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&adaptive_lin_rtol, "-alrtol", "--adaptive-lin-rtol",
"-no-alrtol", "--no-adaptive-lin-rtol",
"Enable or disable adaptive linear solver rtol.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -578,7 +574,6 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
newton_solver.SetPrintLevel(1); // print Newton iterations
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(0.0);
newton_solver.SetAdaptiveLinRtol(2, 0.5, 0.9);
newton_solver.SetMaxIter(10);
}
+2 -10
View File
@@ -5,7 +5,6 @@
// Sample runs: ex14 -m ../data/inline-quad.mesh -o 0
// ex14 -m ../data/star.mesh -r 4 -o 2
// ex14 -m ../data/star-mixed.mesh -r 4 -o 2
// ex14 -m ../data/star-mixed.mesh -r 2 -o 2 -k 0 -e 1
// ex14 -m ../data/escher.mesh -s 1
// ex14 -m ../data/fichera.mesh -s 1 -k 1
// ex14 -m ../data/fichera-mixed.mesh -s 1 -k 1
@@ -45,7 +44,6 @@ int main(int argc, char *argv[])
int order = 1;
double sigma = -1.0;
double kappa = -1.0;
double eta = 0.0;
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -56,12 +54,11 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) >= 0.");
args.AddOption(&sigma, "-s", "--sigma",
"One of the three DG penalty parameters, typically +1/-1."
"One of the two DG penalty parameters, typically +1/-1."
" See the documentation of class DGDiffusionIntegrator.");
args.AddOption(&kappa, "-k", "--kappa",
"One of the three DG penalty parameters, should be positive."
"One of the two DG penalty parameters, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -133,11 +130,6 @@ int main(int argc, char *argv[])
a->AddDomainIntegrator(new DiffusionIntegrator(one));
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
if (eta > 0)
{
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
}
a->Assemble();
a->Finalize();
const SparseMatrix &A = a->SpMat();
+2 -10
View File
@@ -5,7 +5,6 @@
// Sample runs: mpirun -np 4 ex14p -m ../data/inline-quad.mesh -o 0
// mpirun -np 4 ex14p -m ../data/star.mesh -o 2
// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2
// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -k 0 -e 1
// mpirun -np 4 ex14p -m ../data/escher.mesh -s 1
// mpirun -np 4 ex14p -m ../data/fichera.mesh -s 1 -k 1
// mpirun -np 4 ex14p -m ../data/fichera-mixed.mesh -s 1 -k 1
@@ -83,7 +82,6 @@ int main(int argc, char *argv[])
int order = 1;
double sigma = -1.0;
double kappa = -1.0;
double eta = 0.0;
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -97,12 +95,11 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) >= 0.");
args.AddOption(&sigma, "-s", "--sigma",
"One of the three DG penalty parameters, typically +1/-1."
"One of the two DG penalty parameters, typically +1/-1."
" See the documentation of class DGDiffusionIntegrator.");
args.AddOption(&kappa, "-k", "--kappa",
"One of the three DG penalty parameters, should be positive."
"One of the two DG penalty parameters, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -197,11 +194,6 @@ int main(int argc, char *argv[])
a->AddDomainIntegrator(new DiffusionIntegrator(one));
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
if (eta > 0)
{
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
}
a->Assemble();
a->Finalize();
+8 -50
View File
@@ -19,13 +19,6 @@
// mpirun -np 4 ex15p -m ../data/square-disc.mesh
// mpirun -np 4 ex15p -m ../data/escher.mesh -r 2 -tf 0.3
//
// Different estimators:
//
// mpirun -np 4 ex15p -est 0 -e 1e-4
// mpirun -np 4 ex15p -est 1 -e 1e-6
// mpirun -np 4 ex15p -est 1 -o 3 -tf 0.3
// mpirun -np 4 ex15p -est 2 -o 2
//
// Description: Building on Example 6, this example demonstrates dynamic AMR.
// The mesh is adapted to a time-dependent solution by refinement
// as well as by derefinement. For simplicity, the solution is
@@ -35,11 +28,8 @@
// At each outer iteration the right hand side function is changed
// to mimic a time dependent problem. Within each inner iteration
// the problem is solved on a sequence of meshes which are locally
// refined according to a chosen error estimator. Currently there
// are three error estimators supported: A L2 formulation of the
// Zienkiewicz-Zhu error estimator (0), a Kelly error indicator (1)
// and a traditional Zienkiewicz-Zhu error estimator (2). At the
// end of the inner iteration the error estimates are also used to
// refined according to a simple ZZ error estimator. At the end
// of the inner iteration the error estimates are also used to
// identify any elements which may be over-refined and a single
// derefinement step is performed. After each refinement or
// derefinement step a rebalance operation is performed to keep
@@ -97,7 +87,6 @@ int main(int argc, char *argv[])
int nc_limit = 3; // maximum level of hanging nodes
bool visualization = true;
bool visit = false;
int which_estimator = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -118,9 +107,6 @@ int main(int argc, char *argv[])
"Maximum level of hanging nodes.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&which_estimator, "-est", "--estimator",
"Which estimator to use: "
"0 = L2ZZ, 1 = Kelly, 2 = ZZ. Defaults to L2ZZ.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -228,43 +214,17 @@ int main(int argc, char *argv[])
// provide the method ComputeElementFlux. We supply an L2 space for the
// discontinuous flux and an H(div) space for the smoothed flux.
L2_FECollection flux_fec(order, dim);
ParFiniteElementSpace flux_fes(&pmesh, &flux_fec, sdim);
RT_FECollection smooth_flux_fec(order-1, dim);
ErrorEstimator* estimator;
switch (which_estimator)
{
case 1:
{
auto flux_fes = new ParFiniteElementSpace(&pmesh, &flux_fec, sdim);
estimator = new KellyErrorEstimator(*integ, x, flux_fes);
break;
}
case 2:
{
auto flux_fes = new ParFiniteElementSpace(&pmesh, &fec, sdim);
estimator = new ZienkiewiczZhuEstimator(*integ, x, flux_fes);
break;
}
default:
if (myid == 0)
{
std::cout << "Unkown estimator. Falling back to L2ZZ." << std::endl;
}
case 0:
{
auto flux_fes = new ParFiniteElementSpace(&pmesh, &flux_fec, sdim);
auto smooth_flux_fes = new ParFiniteElementSpace(&pmesh, &smooth_flux_fec);
estimator = new L2ZienkiewiczZhuEstimator(*integ, x, flux_fes, smooth_flux_fes);
break;
}
}
ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec);
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, smooth_flux_fes);
// 11. As in Example 6p, we also need a refiner. This time the refinement
// strategy is based on a fixed threshold that is applied locally to each
// element. The global threshold is turned off by setting the total error
// fraction to zero. We also enforce a maximum refinement ratio between
// adjacent elements.
ThresholdRefiner refiner(*estimator);
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.0); // use purely local threshold
refiner.SetLocalErrorGoal(max_elem_error);
refiner.PreferConformingRefinement();
@@ -273,7 +233,7 @@ int main(int argc, char *argv[])
// 12. A derefiner selects groups of elements that can be coarsened to form
// a larger element. A conservative enough threshold needs to be set to
// prevent derefining elements that would immediately be refined again.
ThresholdDerefiner derefiner(*estimator);
ThresholdDerefiner derefiner(estimator);
derefiner.SetThreshold(hysteresis * max_elem_error);
derefiner.SetNCLimit(nc_limit);
@@ -356,7 +316,7 @@ int main(int argc, char *argv[])
refiner.Apply(pmesh);
if (myid == 0)
{
cout << ", total error: " << estimator->GetTotalError() << endl;
cout << ", total error: " << estimator.GetTotalError() << endl;
}
// 21. Quit the AMR loop if the termination criterion has been met
@@ -386,8 +346,6 @@ int main(int argc, char *argv[])
}
}
delete estimator;
// 25. Exit
MPI_Finalize();
return 0;
-435
View File
@@ -1,435 +0,0 @@
// MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p_complex
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Exact solution, E, and r.h.s., f. See below for implementation.
double E_exact(const Vector &);
void gradE_exact(const Vector &, Vector &);
double f_exact(const Vector &);
double freq = 1.0, kappa;
int dim;
#define COMPLEX_VERSION
#define NEUMANN
const double omega = 1.4;
const double eps = 1.0e-8;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
" solution.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 4. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
// spaces on them.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 0;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
pmesh->ReorientTetMesh();
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new H1_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
#ifndef NEUMANN
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
#endif
//const double imscale = 0.0;
const double imscale = -omega;
Coefficient *im = new ConstantCoefficient(imscale); // im part
//Coefficient *im = new ConstantCoefficient(0.0); // im part
FunctionCoefficient E_coef(E_exact);
VectorFunctionCoefficient grad_E(sdim, gradE_exact);
ProductCoefficient omegaE(imscale, E_coef);
// 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
FunctionCoefficient f(f_exact);
#ifdef COMPLEX_VERSION
ParComplexLinearForm *b = new ParComplexLinearForm(fespace);
b->AddDomainIntegrator(new DomainLFIntegrator(f), NULL);
#ifdef NEUMANN
b->AddBoundaryIntegrator(NULL, new BoundaryNormalLFIntegrator(grad_E));
#endif
b->AddBoundaryIntegrator(NULL, new BoundaryLFIntegrator(omegaE)); // im part
#endif
b->Assemble();
// 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary edges will be used
// when eliminating the non-homogeneous boundary condition to modify the
// r.h.s. vector b.
/*
ParGridFunction x(fespace);
VectorFunctionCoefficient E(sdim, E_exact);
x.ProjectCoefficient(E);
*/
#ifdef COMPLEX_VERSION
// Complex version
ParComplexGridFunction x(fespace);
x = 0.0;
ConstantCoefficient E_im(0.0);
//x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
x.ProjectCoefficient(E_coef, E_im);
#endif
// 11. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl + sigma I, by adding the curl-curl and the
// mass domain integrators.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *epscoef = new ConstantCoefficient(eps);
Coefficient *imabs = new ConstantCoefficient(fabs(imscale)); // im part
#ifdef COMPLEX_VERSION
// Complex version
ParSesquilinearForm *a = new ParSesquilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DiffusionIntegrator(*muinv), NULL);
a->AddDomainIntegrator(new MassIntegrator(*epscoef), NULL);
a->AddBoundaryIntegrator(NULL, new MassIntegrator(*im)); // im part
#endif
// 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
//if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
OperatorPtr A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
ParBilinearForm a_Re(fespace);
a_Re.AddDomainIntegrator(new DiffusionIntegrator(*muinv));
a_Re.AddDomainIntegrator(new MassIntegrator(*epscoef));
if (pa) { a_Re.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a_Re.Assemble();
OperatorPtr A_Re;
a_Re.FormSystemMatrix(ess_tdof_list, A_Re);
ParBilinearForm a_Im(fespace);
a_Im.AddBoundaryIntegrator(new MassIntegrator(*imabs));
a_Im.Assemble();
OperatorPtr A_Im;
a_Im.FormSystemMatrix(ess_tdof_list, A_Im);
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
// (in the full assembly case) or CG with Jacobi preconditioner (in the
// partial assembly case).
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = fespace->GetTrueVSize();
offsets[2] = fespace->GetTrueVSize();
offsets.PartialSum();
//OperatorJacobiSmoother massJacobi(a_Im, ess_tdof_list);
StopWatch sw;
sw.Clear();
sw.Start();
if (pa) // Jacobi preconditioning in partial assembly mode
{
MFEM_VERIFY(false, "TODO");
//OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(1000);
cg.SetPrintLevel(1);
cg.SetOperator(*A);
//cg.SetPreconditioner(Jacobi);
cg.Mult(B, X);
}
else
{
if (myid == 0)
{
cout << "Size of linear system: "
<< A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
}
HypreBoomerAMG amg(*A_Re.As<HypreParMatrix>());
#ifdef COMPLEX_VERSION
BlockDiagonalPreconditioner BlockDP(offsets);
BlockDP.SetDiagonalBlock(0, &amg);
BlockDP.SetDiagonalBlock(1, &amg);
Complex_PMHSS PMHSS(A_Re.Ptr(), A_Im.Ptr(), &BlockDP, NULL, 1.0);
ComplexOperator AspdComplex(A_Re.Ptr(), A_Im.Ptr(), false, false);
GMRESSolver PMHSSgmres(MPI_COMM_WORLD);
PMHSSgmres.SetPrintLevel(1);
PMHSSgmres.SetKDim(100);
PMHSSgmres.SetMaxIter(100);
PMHSSgmres.SetRelTol(1e-6);
PMHSSgmres.SetAbsTol(0.0);
PMHSSgmres.SetOperator(AspdComplex);
PMHSSgmres.SetPreconditioner(PMHSS);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPrintLevel(1);
gmres.SetKDim(1000);
gmres.SetMaxIter(100);
gmres.SetRelTol(1e-8);
gmres.SetAbsTol(0.0);
gmres.SetOperator(*A);
//gmres.SetPreconditioner(BlockDP);
gmres.SetPreconditioner(PMHSS);
//gmres.SetPreconditioner(PMHSSgmres);
#else
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPrintLevel(1);
gmres.SetKDim(1000);
gmres.SetMaxIter(100);
gmres.SetRelTol(1e-8);
gmres.SetAbsTol(0.0);
gmres.SetOperator(*A);
gmres.SetPreconditioner(ams);
#endif
gmres.Mult(B, X);
}
sw.Stop();
mfem::out << "Total solve time " <<sw.RealTime() << endl;
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 15. Compute and print the L^2 norm of the error.
{
#ifdef COMPLEX_VERSION
double err = x.real().ComputeL2Error(E_coef);
#else
double err = x.ComputeL2Error(E_coef);
#endif
if (myid == 0)
{
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
}
}
// 16. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
#ifdef COMPLEX_VERSION
x.real().Save(sol_ofs);
#else
x.Save(sol_ofs);
#endif
}
// 17. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
#ifdef COMPLEX_VERSION
sol_sock << "solution\n" << *pmesh << x.real() << flush;
#else
sol_sock << "solution\n" << *pmesh << x << flush;
#endif
}
// 18. Free the used memory.
delete a;
delete muinv;
delete b;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
#define VERSION_COS
double E_exact(const Vector &x)
{
if (dim == 3)
{
#ifdef VERSION_COS
return cos(kappa * x(0)) * cos(kappa * x(1)) * cos(kappa * x(2));
#else
return sin(kappa * x(0)) * sin(kappa * x(1)) * sin(kappa * x(2));
#endif
}
else
{
return 0.0;
}
}
void gradE_exact(const Vector &x, Vector &grad)
{
if (dim == 3)
{
#ifdef VERSION_COS
grad(0) = -kappa * sin(kappa * x(0)) * cos(kappa * x(1)) * cos(kappa * x(2));
grad(1) = -kappa * sin(kappa * x(1)) * cos(kappa * x(0)) * cos(kappa * x(2));
grad(2) = -kappa * sin(kappa * x(2)) * cos(kappa * x(0)) * cos(kappa * x(1));
#else
grad(0) = kappa * cos(kappa * x(0)) * sin(kappa * x(1)) * sin(kappa * x(2));
grad(1) = kappa * cos(kappa * x(1)) * sin(kappa * x(0)) * sin(kappa * x(2));
grad(2) = kappa * cos(kappa * x(2)) * sin(kappa * x(0)) * sin(kappa * x(1));
#endif
}
else
{
MFEM_VERIFY(false, "");
}
}
// (grad u, grad v) + eps (u, v) = <grad u . n, v> - (div grad u, v) + eps (u, v)
double f_exact(const Vector &x)
{
if (dim == 3)
{
const double c = 3.0 * kappa * kappa;
#ifdef VERSION_COS
return (eps + c) * cos(kappa * x(0)) * cos(kappa * x(1)) * cos(kappa * x(2));
#else
return (eps + c) * sin(kappa * x(0)) * sin(kappa * x(1)) * sin(kappa * x(2));
#endif
}
else
{
return 0.0;
}
}
+33 -81
View File
@@ -10,10 +10,6 @@
// ex25 -o 2 -f 8.0 -ref 3 -prob 4 -m ../data/inline-quad.mesh
// ex25 -o 2 -f 2.0 -ref 1 -prob 4 -m ../data/inline-hex.mesh
//
// Device sample runs:
// ex25 -o 2 -f 8.0 -ref 3 -prob 4 -m ../data/inline-quad.mesh -pa -d cuda
// ex25 -o 2 -f 2.0 -ref 1 -prob 4 -m ../data/inline-hex.mesh -pa -d cuda
//
// Description: This example code solves a simple electromagnetic wave
// propagation problem corresponding to the second order
// indefinite Maxwell equation
@@ -161,10 +157,7 @@ int main(int argc, char *argv[])
int iprob = 4;
double freq = 5.0;
bool herm_conv = true;
bool umf_solver = false;
bool visualization = 1;
bool pa = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -183,28 +176,15 @@ int main(int argc, char *argv[])
"Frequency (in Hz).");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
#ifdef MFEM_USE_SUITESPARSE
args.AddOption(&umf_solver, "-umf", "--umfpack", "-no-umf",
"--no-umfpack", "Use the UMFPack Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (iprob > 4) { iprob = 4; }
prob = (prob_type)iprob;
// 2. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
device.Print();
// 3. Setup the mesh
// 2. Setup the mesh
if (!mesh_file)
{
exact_known = true;
@@ -245,7 +225,7 @@ int main(int argc, char *argv[])
// Setup PML length
Array2D<double> length(dim, 2); length = 0.0;
// 4. Setup the Cartesian PML region.
// 3. Setup the Cartesian PML region.
switch (prob)
{
case disc:
@@ -271,19 +251,19 @@ int main(int argc, char *argv[])
comp_domain_bdr = pml->GetCompDomainBdr();
domain_bdr = pml->GetDomainBdr();
// 5. Refine the mesh to increase the resolution.
// 4. Refine the mesh to increase the resolution.
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
// 6. Reorient mesh in case of a tet mesh
// 5. Reorient mesh in case of a tet mesh
mesh->ReorientTetMesh();
// Set element attributes in order to distinguish elements in the PML region
pml->SetAttributes(mesh);
// 7. Define a finite element space on the mesh. Here we use the Nedelec
// 6. Define a finite element space on the mesh. Here we use the Nedelec
// finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
@@ -291,7 +271,7 @@ int main(int argc, char *argv[])
cout << "Number of finite element unknowns: " << size << endl;
// 8. Determine the list of true essential boundary dofs. In this example,
// 7. Determine the list of true essential boundary dofs. In this example,
// the boundary conditions are defined based on the specific mesh and the
// problem type.
Array<int> ess_tdof_list;
@@ -333,12 +313,12 @@ int main(int argc, char *argv[])
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 9. Setup Complex Operator convention
// 8. Setup Complex Operator convention
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 10. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system.
// 9. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system.
VectorFunctionCoefficient f(dim, source);
ComplexLinearForm b(fespace, conv);
if (prob == load_src)
@@ -348,7 +328,7 @@ int main(int argc, char *argv[])
b.Vector::operator=(0.0);
b.Assemble();
// 11. Define the solution vector x as a complex finite element grid function
// 10. Define the solution vector x as a complex finite element grid function
// corresponding to fespace.
ComplexGridFunction x(fespace);
x = 0.0;
@@ -356,7 +336,7 @@ int main(int argc, char *argv[])
VectorFunctionCoefficient E_Im(dim, E_bdr_data_Im);
x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
// 12. Set up the sesquilinear form a(.,.)
// 11. Set up the sesquilinear form a(.,.)
//
// In Comp
// Domain: 1/mu (Curl E, Curl F) - omega^2 * epsilon (E,F)
@@ -410,35 +390,32 @@ int main(int argc, char *argv[])
a.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_Re),
new VectorFEMassIntegrator(restr_c2_Im));
// 13. Assemble the bilinear form and the corresponding linear system,
// 12. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: assembly, eliminating
// boundary conditions, applying conforming constraints for
// non-conforming AMR, etc.
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a.Assemble(0);
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
// 14. Solve using a direct or an iterative solver
// 13. Solve using a direct or an iterative solver
#ifdef MFEM_USE_SUITESPARSE
if (!pa && umf_solver)
{
ComplexUMFPackSolver csolver(*A.As<ComplexSparseMatrix>());
csolver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
csolver.SetPrintLevel(1);
csolver.Mult(B, X);
}
#endif
// 14a. Set up the Bilinear form a(.,.) for the preconditioner
#else
// 13a. Set up the Bilinear form a(.,.) for the preconditioner
//
// In Comp
// Domain: 1/mu (Curl E, Curl F) + omega^2 * epsilon (E,F)
//
// In PML: 1/mu (abs(1/det(J) J^T J) Curl E, Curl F)
// + omega^2 * epsilon (abs(det(J) * (J^T J)^-1) * E, F)
if (pa || !umf_solver)
{
ConstantCoefficient absomeg(pow(omega, 2) * epsilon);
RestrictedCoefficient restr_absomeg(absomeg,attr);
@@ -458,57 +435,39 @@ int main(int argc, char *argv[])
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_abs));
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_abs));
if (pa) { prec.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
prec.Assemble();
// 14b. Define and apply a GMRES solver for AU=B with a block diagonal
// preconditioner based on the Gauss-Seidel or Jacobi sparse smoother.
OperatorPtr PCOpAh;
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
// 13b. Define and apply a GMRES solver for AU=B with a block diagonal
// preconditioner based on the Gauss-Seidel sparse smoother.
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = fespace->GetTrueVSize();
offsets[2] = fespace->GetTrueVSize();
offsets.PartialSum();
Operator *pc_r = nullptr;
Operator *pc_i = nullptr;
int s = (conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0;
if (pa)
{
// Jacobi Smoother
OperatorJacobiSmoother *d00 = new OperatorJacobiSmoother(prec, ess_tdof_list);
ScaledOperator *d11 = new ScaledOperator(d00, s);
pc_r = d00;
pc_i = d11;
}
else
{
OperatorPtr PCOpAh;
prec.SetDiagonalPolicy(mfem::Operator::DIAG_ONE);
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
// Gauss-Seidel Smoother
GSSmoother *gs00 = new GSSmoother(*PCOpAh.As<SparseMatrix>());
ScaledOperator *gs11 = new ScaledOperator(gs00, s);
pc_r = gs00;
pc_i = gs11;
}
BlockDiagonalPreconditioner BlockDP(offsets);
BlockDP.SetDiagonalBlock(0, pc_r);
BlockDP.SetDiagonalBlock(1, pc_i);
GSSmoother gs00(*PCOpAh.As<SparseMatrix>());
BlockDiagonalPreconditioner BlockGS(offsets);
ScaledOperator gs11(&gs00,
(conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0);
BlockGS.SetDiagonalBlock(0,&gs00);
BlockGS.SetDiagonalBlock(1,&gs11);
GMRESSolver gmres;
gmres.SetPrintLevel(1);
gmres.SetKDim(200);
gmres.SetMaxIter(pa ? 5000 : 2000);
gmres.SetMaxIter(2000);
gmres.SetRelTol(1e-5);
gmres.SetAbsTol(0.0);
gmres.SetOperator(*A);
gmres.SetPreconditioner(BlockDP);
gmres.SetPreconditioner(BlockGS);
gmres.Mult(B, X);
}
#endif
// 15. Recover the solution as a finite element grid function and compute the
// 14. Recover the solution as a finite element grid function and compute the
// errors if the exact solution is known.
a.RecoverFEMSolution(X, b, x);
@@ -545,7 +504,7 @@ int main(int argc, char *argv[])
<< sqrt(L2Error_Re*L2Error_Re + L2Error_Im*L2Error_Im) << "\n\n";
}
// 16. Save the refined mesh and the solution. This output can be viewed
// 15. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m mesh -g sol".
{
ofstream mesh_ofs("ex25.mesh");
@@ -560,7 +519,7 @@ int main(int argc, char *argv[])
x.imag().Save(sol_i_ofs);
}
// 17. Send the solution by socket to a GLVis server.
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
// Define visualization keys for GLVis (see GLVis documentation)
@@ -611,7 +570,7 @@ int main(int argc, char *argv[])
}
}
// 18. Free the used memory.
// 17. Free the used memory.
delete pml;
delete fespace;
delete fec;
@@ -957,14 +916,7 @@ void CartesianPML::SetBoundaries()
void CartesianPML::SetAttributes(Mesh *mesh_)
{
// Initialize bdr attributes
for (int i = 0; i < mesh_->GetNBE(); ++i)
{
mesh_->GetBdrElement(i)->SetAttribute(i+1);
}
int nrelem = mesh_->GetNE();
elems.SetSize(nrelem);
// Loop through the elements and identify which of them are in the PML
+32 -70
View File
@@ -10,10 +10,6 @@
// mpirun -np 4 ex25p -o 2 -f 8.0 -rs 2 -rp 2 -prob 4 -m ../data/inline-quad.mesh
// mpirun -np 4 ex25p -o 2 -f 2.0 -rs 1 -rp 1 -prob 4 -m ../data/inline-hex.mesh
//
// Device sample runs:
// mpirun -np 4 ex25p -o 1 -f 3.0 -rs 3 -rp 1 -prob 2 -pa -d cuda
// mpirun -np 4 ex25p -o 2 -f 1.0 -rs 1 -rp 1 -prob 3 -pa -d cuda
//
// Description: This example code solves a simple electromagnetic wave
// propagation problem corresponding to the second order
// indefinite Maxwell equation
@@ -171,8 +167,6 @@ int main(int argc, char *argv[])
bool slu_solver = false;
bool mumps_solver = false;
bool visualization = 1;
bool pa = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -204,10 +198,6 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (slu_solver && mumps_solver)
{
@@ -221,12 +211,7 @@ int main(int argc, char *argv[])
if (iprob > 4) { iprob = 4; }
prob = (prob_type)iprob;
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 4. Setup the (serial) mesh on all processors.
// 3. Setup the (serial) mesh on all processors.
if (!mesh_file)
{
exact_known = true;
@@ -274,7 +259,7 @@ int main(int argc, char *argv[])
// Setup PML length
Array2D<double> length(dim, 2); length = 0.0;
// 5. Setup the Cartesian PML region.
// 4. Setup the Cartesian PML region.
switch (prob)
{
case disc:
@@ -300,13 +285,13 @@ int main(int argc, char *argv[])
comp_domain_bdr = pml->GetCompDomainBdr();
domain_bdr = pml->GetDomainBdr();
// 6. Refine the serial mesh on all processors to increase the resolution.
// 5. Refine the serial mesh on all processors to increase the resolution.
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
// 7. Define a parallel mesh by a partitioning of the serial mesh.
// 6. Define a parallel mesh by a partitioning of the serial mesh.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
@@ -316,13 +301,13 @@ int main(int argc, char *argv[])
}
}
// 7a. Reorient mesh in case of a tet mesh
// 6a. Reorient mesh in case of a tet mesh
pmesh->ReorientTetMesh();
// 8. Set element attributes in order to distinguish elements in the PML
// 7. Set element attributes in order to distinguish elements in the PML
pml->SetAttributes(pmesh);
// 9. Define a parallel finite element space on the parallel mesh. Here we
// 8. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
@@ -332,9 +317,9 @@ int main(int argc, char *argv[])
cout << "Number of finite element unknowns: " << size << endl;
}
// 10. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// based on the specific mesh and the problem type.
// 9. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// based on the specific mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh->bdr_attributes.Size())
@@ -374,11 +359,11 @@ int main(int argc, char *argv[])
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 11. Setup Complex Operator convention
// 10. Setup Complex Operator convention
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 12. Set up the parallel linear form b(.) which corresponds to the
// 11. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system.
VectorFunctionCoefficient f(dim, source);
ParComplexLinearForm b(fespace, conv);
@@ -389,7 +374,7 @@ int main(int argc, char *argv[])
b.Vector::operator=(0.0);
b.Assemble();
// 13. Define the solution vector x as a parallel complex finite element grid
// 12. Define the solution vector x as a parallel complex finite element grid
// function corresponding to fespace.
ParComplexGridFunction x(fespace);
x = 0.0;
@@ -397,7 +382,7 @@ int main(int argc, char *argv[])
VectorFunctionCoefficient E_Im(dim, E_bdr_data_Im);
x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
// 14. Set up the parallel sesquilinear form a(.,.)
// 13. Set up the parallel sesquilinear form a(.,.)
//
// In Comp
// Domain: 1/mu (Curl E, Curl F) - omega^2 * epsilon (E,F)
@@ -451,20 +436,19 @@ int main(int argc, char *argv[])
a.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_Re),
new VectorFEMassIntegrator(restr_c2_Im));
// 15. Assemble the parallel bilinear form and the corresponding linear
// 14. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, etc.
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a.Assemble();
OperatorPtr Ah;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
// 16. Solve using a direct or an iterative solver
// 15. Solve using a direct or an iterative solver
#ifdef MFEM_USE_SUPERLU
if (!pa && slu_solver)
if (slu_solver)
{
// Transform to monolithic HypreParMatrix
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
@@ -479,7 +463,7 @@ int main(int argc, char *argv[])
}
#endif
#ifdef MFEM_USE_MUMPS
if (!pa && mumps_solver)
if (mumps_solver)
{
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
MUMPSSolver mumps;
@@ -497,7 +481,7 @@ int main(int argc, char *argv[])
//
// In PML: 1/mu (abs(1/det(J) J^T J) Curl E, Curl F)
// + omega^2 * epsilon (abs(det(J) * (J^T J)^-1) * E, F)
if (pa || (!slu_solver && !mumps_solver))
if (!slu_solver && !mumps_solver)
{
ConstantCoefficient absomeg(pow(omega, 2) * epsilon);
RestrictedCoefficient restr_absomeg(absomeg,attr);
@@ -517,9 +501,11 @@ int main(int argc, char *argv[])
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_abs));
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_abs));
if (pa) { prec.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
prec.Assemble();
OperatorPtr PCOpAh;
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
// 16b. Define and apply a parallel GMRES solver for AU=B with a block
// diagonal preconditioner based on hypre's AMS preconditioner.
Array<int> offsets(3);
@@ -528,41 +514,21 @@ int main(int argc, char *argv[])
offsets[2] = fespace->GetTrueVSize();
offsets.PartialSum();
Operator *pc_r = nullptr;
Operator *pc_i = nullptr;
int s = (conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0;
if (pa)
{
// Jacobi Smoother
OperatorJacobiSmoother *d00 = new OperatorJacobiSmoother(prec, ess_tdof_list);
ScaledOperator *d11 = new ScaledOperator(d00, s);
pc_r = d00;
pc_i = d11;
}
else
{
OperatorPtr PCOpAh;
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
// Hypre AMS
HypreAMS *ams00 = new HypreAMS(*PCOpAh.As<HypreParMatrix>(), fespace);
ScaledOperator *ams11 = new ScaledOperator(ams00, s);
pc_r = ams00;
pc_i = ams11;
}
BlockDiagonalPreconditioner BlockDP(offsets);
BlockDP.SetDiagonalBlock(0, pc_r);
BlockDP.SetDiagonalBlock(1, pc_i);
HypreAMS ams00(*PCOpAh.As<HypreParMatrix>(),fespace);
BlockDiagonalPreconditioner BlockAMS(offsets);
ScaledOperator ams11(&ams00,
(conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0);
BlockAMS.SetDiagonalBlock(0,&ams00);
BlockAMS.SetDiagonalBlock(1,&ams11);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPrintLevel(1);
gmres.SetKDim(200);
gmres.SetMaxIter(pa ? 5000 : 2000);
gmres.SetMaxIter(2000);
gmres.SetRelTol(1e-5);
gmres.SetAbsTol(0.0);
gmres.SetOperator(*Ah);
gmres.SetPreconditioner(BlockDP);
gmres.SetPreconditioner(BlockAMS);
gmres.Mult(B, X);
}
@@ -1037,12 +1003,8 @@ void CartesianPML::SetBoundaries()
void CartesianPML::SetAttributes(ParMesh *pmesh)
{
// Initialize bdr attributes
for (int i = 0; i < pmesh->GetNBE(); ++i)
{
pmesh->GetBdrElement(i)->SetAttribute(i+1);
}
int myid;
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
int nrelem = pmesh->GetNE();
// Initialize list with 1
+2 -2
View File
@@ -40,7 +40,7 @@ using namespace mfem;
// in the FiniteElementSpaceHierarchy. The preconditioner uses a CG solver on
// the coarsest level and second order Chebyshev accelerated smoothers on the
// other levels.
class DiffusionMultigrid : public GeometricMultigrid
class DiffusionMultigrid : public Multigrid
{
private:
ConstantCoefficient one;
@@ -49,7 +49,7 @@ public:
// Constructs a diffusion multigrid for the given FiniteElementSpaceHierarchy
// and the array of essential boundaries
DiffusionMultigrid(FiniteElementSpaceHierarchy& fespaces, Array<int>& ess_bdr)
: GeometricMultigrid(fespaces), one(1.0)
: Multigrid(fespaces), one(1.0)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
+2 -2
View File
@@ -37,7 +37,7 @@ using namespace mfem;
// all spaces except the coarsest one in the ParFiniteElementSpaceHierarchy.
// The multigrid uses a PCG solver preconditioned with AMG on the coarsest level
// and second order Chebyshev accelerated smoothers on the other levels.
class DiffusionMultigrid : public GeometricMultigrid
class DiffusionMultigrid : public Multigrid
{
private:
ConstantCoefficient one;
@@ -48,7 +48,7 @@ public:
// and the array of essential boundaries
DiffusionMultigrid(ParFiniteElementSpaceHierarchy& fespaces,
Array<int>& ess_bdr)
: GeometricMultigrid(fespaces), one(1.0)
: Multigrid(fespaces), one(1.0)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
+10 -20
View File
@@ -22,7 +22,6 @@
//
// Device sample runs:
// mpirun -np 4 ex3p -m ../data/star.mesh -pa -d cuda
// mpirun -np 4 ex3p -m ../data/star.mesh -no-pa -d cuda
// mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-cuda
// mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-omp
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh -pa -d cuda
@@ -69,10 +68,7 @@ int main(int argc, char *argv[])
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
#ifdef MFEM_USE_AMGX
bool useAmgX = false;
#endif
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -90,11 +86,6 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
#ifdef MFEM_USE_AMGX
args.AddOption(&useAmgX, "-amgx", "--useAmgX", "-no-amgx",
"--no-useAmgX",
"Enable or disable AmgX in MatrixFreeAMS.");
#endif
args.Parse();
if (!args.Good())
@@ -167,10 +158,9 @@ int main(int argc, char *argv[])
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
@@ -214,20 +204,20 @@ int main(int argc, char *argv[])
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
// 13. Solve the system AX=B using PCG with an AMS preconditioner.
if (pa)
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
// (in the full assembly case) or CG with Jacobi preconditioner (in the
// partial assembly case).
if (pa) // Jacobi preconditioning in partial assembly mode
{
#ifdef MFEM_USE_AMGX
MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr, useAmgX);
#else
MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr);
#endif
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(1000);
cg.SetPrintLevel(1);
cg.SetOperator(*A);
cg.SetPreconditioner(ams);
cg.SetPreconditioner(Jacobi);
cg.Mult(B, X);
}
else
-517
View File
@@ -1,517 +0,0 @@
// MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p_complex
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void curlE_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;
#define COMPLEX_VERSION
#define NEUMANN
#define INDEFINITE
const double omega = 1.4;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
" solution.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 4. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
// spaces on them.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 0;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
pmesh->ReorientTetMesh();
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 0;
#ifndef NEUMANN
if (pmesh->bdr_attributes.Size())
{
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
#endif
//const double imscale = 0.0;
const double imscale = omega;
Coefficient *im = new ConstantCoefficient(imscale); // im part
//Coefficient *im = new ConstantCoefficient(0.0); // im part
VectorFunctionCoefficient E_Re(sdim, E_exact);
VectorFunctionCoefficient curlE_Re(sdim, curlE_exact);
ScalarVectorProductCoefficient omegaE(imscale, E_Re); // im part
//ScalarVectorProductCoefficient omegaE(0.0, E_Re); // im part
// 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
VectorFunctionCoefficient f(sdim, f_exact);
#ifdef COMPLEX_VERSION
ParComplexLinearForm *b = new ParComplexLinearForm(fespace);
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f), NULL);
b->AddBoundaryIntegrator(NULL,
new VectorFEDomainLFIntegrator(omegaE)); // im part
#else
// Real version
ParLinearForm *b = new ParLinearForm(fespace);
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
#endif
#ifdef NEUMANN
b->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(curlE_Re),
NULL);
#endif
b->Assemble();
// 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary edges will be used
// when eliminating the non-homogeneous boundary condition to modify the
// r.h.s. vector b.
/*
ParGridFunction x(fespace);
VectorFunctionCoefficient E(sdim, E_exact);
x.ProjectCoefficient(E);
*/
#ifdef COMPLEX_VERSION
// Complex version
ParComplexGridFunction x(fespace);
x = 0.0;
Vector zero(sdim);
zero = 0.0;
VectorConstantCoefficient E_Im(zero);
//x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
x.ProjectCoefficient(E_Re, E_Im);
#else
ParGridFunction x(fespace);
x = 0.0;
x.ProjectCoefficient(E_Re);
#endif
// 11. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl + sigma I, by adding the curl-curl and the
// mass domain integrators.
Coefficient *muinv = new ConstantCoefficient(1.0);
#ifdef INDEFINITE
Coefficient *sigma = new ConstantCoefficient(
-omega*omega); // indefinite -, definite +
#else
Coefficient *sigma = new ConstantCoefficient(
omega*omega); // indefinite -, definite +
#endif
Coefficient *abssigma = new ConstantCoefficient(omega*omega);
Coefficient *imabs = new ConstantCoefficient(imscale); // im part
//Coefficient *imabs = new ConstantCoefficient(0.0); // im part
//Coefficient *im = new ConstantCoefficient(0.0);
#ifdef COMPLEX_VERSION
// Complex version
ParSesquilinearForm *a = new ParSesquilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv), NULL);
//a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma), new VectorFEMassIntegrator(*im));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma), NULL);
a->AddBoundaryIntegrator(NULL, new VectorFEMassIntegrator(*im)); // im part
#else
// Real version
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
//a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma), new VectorFEMassIntegrator(*im));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
#endif
// 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
//if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
OperatorPtr A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
ParBilinearForm a_Re(fespace);
a_Re.AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
a_Re.AddDomainIntegrator(new VectorFEMassIntegrator(*abssigma));
//if (pa) { a_Re.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a_Re.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a_Re.Assemble();
OperatorPtr A_Re;
a_Re.FormSystemMatrix(ess_tdof_list, A_Re);
ParBilinearForm a_Im(fespace);
a_Im.AddBoundaryIntegrator(new VectorFEMassIntegrator(*imabs));
a_Im.Assemble();
OperatorPtr A_Im;
a_Im.FormSystemMatrix(ess_tdof_list, A_Im);
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
// (in the full assembly case) or CG with Jacobi preconditioner (in the
// partial assembly case).
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = fespace->GetTrueVSize();
offsets[2] = fespace->GetTrueVSize();
offsets.PartialSum();
//OperatorJacobiSmoother massJacobi(a_Im, ess_tdof_list);
StopWatch sw;
sw.Clear();
sw.Start();
if (pa) // Jacobi preconditioning in partial assembly mode
{
MFEM_VERIFY(false, "TODO");
//OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(1000);
cg.SetPrintLevel(1);
cg.SetOperator(*A);
//cg.SetPreconditioner(Jacobi);
cg.Mult(B, X);
}
else
{
if (myid == 0)
{
cout << "Size of linear system: "
<< A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
}
//HypreAMS ams(*A_Re.As<HypreParMatrix>(), fespace);
// One option is to use the standard real-valued MatrixFreeAMS to precondition
// the real part of the complex system in the PMHSS preconditioner (BlockDiagonalPreconditioner).
// Another option is to use complex MatrixFreeAMS to precondition the
// complex system without PMHSS and without a BlockDiagonalPreconditioner.
//#define COMPLEX_AMS
#ifdef MFEM_USE_AMGX
bool useAmgX = false;
cout << "Built with AMGX, using AMGX " << useAmgX << endl;
MatrixFreeAMS ams(a_Re, *A_Re, *fespace, muinv, abssigma, im, imabs, NULL,
ess_bdr, useAmgX);
MatrixFreeAMS ams(a_Re, *A_Re, *fespace, muinv, abssigma, NULL, NULL, ess_bdr,
useAmgX);
#ifdef COMPLEX_AMS
MFEM_VERIFY(false, "TODO");
#endif
#else
cout << "Not built with AMGX" << endl;
#ifdef COMPLEX_AMS
MatrixFreeAMS ams(a_Re, *A_Re, A.Ptr(), *fespace, muinv, abssigma, im, imabs,
NULL, ess_bdr);
#else
MatrixFreeAMS ams(a_Re, *A_Re, NULL, *fespace, muinv, abssigma, NULL, NULL,
NULL, ess_bdr);
#endif
#endif
#ifdef COMPLEX_VERSION
#ifdef COMPLEX_AMS
//MFEM_VERIFY(false, "TODO");
#else
BlockDiagonalPreconditioner BlockDP(offsets);
BlockDP.SetDiagonalBlock(0, &ams);
BlockDP.SetDiagonalBlock(1, &ams);
/*
BlockDiagonalPreconditioner BlockDP_Im(offsets);
BlockDP_Im.SetDiagonalBlock(0, &massJacobi); // TODO: this won't work if it has zeros on diagonal
BlockDP_Im.SetDiagonalBlock(1, &massJacobi);
*/
//Complex_PMHSS PMHSS(A_Re, A_Im, &BlockDP, &BlockDP_Im);
//Complex_PMHSS PMHSS(A_Re, A_Im, &BlockDP, NULL, 2.0 * omega);
//Complex_PMHSS PMHSS(A_Re, A_Im, &BlockDP, NULL, omega);
Complex_PMHSS PMHSS(A_Re.Ptr(), A_Im.Ptr(), &BlockDP, NULL, 1.0);
ComplexOperator AspdComplex(A_Re.Ptr(), A_Im.Ptr(), false, false);
GMRESSolver PMHSSgmres(MPI_COMM_WORLD);
PMHSSgmres.SetPrintLevel(1);
PMHSSgmres.SetKDim(100);
PMHSSgmres.SetMaxIter(100);
PMHSSgmres.SetRelTol(1e-6);
PMHSSgmres.SetAbsTol(0.0);
PMHSSgmres.SetOperator(AspdComplex);
PMHSSgmres.SetPreconditioner(PMHSS);
#endif
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPrintLevel(1);
gmres.SetKDim(1000);
gmres.SetMaxIter(100);
gmres.SetRelTol(1e-8);
gmres.SetAbsTol(0.0);
gmres.SetOperator(*A);
//gmres.SetPreconditioner(BlockDP);
#ifdef COMPLEX_AMS
//MFEM_VERIFY(false, "TODO");
gmres.SetPreconditioner(ams);
#else
gmres.SetPreconditioner(PMHSS);
//gmres.SetPreconditioner(PMHSSgmres);
#endif
#else
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPrintLevel(1);
gmres.SetKDim(1000);
gmres.SetMaxIter(100);
gmres.SetRelTol(1e-8);
gmres.SetAbsTol(0.0);
gmres.SetOperator(*A);
gmres.SetPreconditioner(ams);
#endif
gmres.Mult(B, X);
}
sw.Stop();
mfem::out << "Total solve time " <<sw.RealTime() << endl;
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 15. Compute and print the L^2 norm of the error.
{
#ifdef COMPLEX_VERSION
double err = x.real().ComputeL2Error(E_Re);
#else
double err = x.ComputeL2Error(E_Re);
#endif
if (myid == 0)
{
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
}
}
// 16. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
#ifdef COMPLEX_VERSION
x.real().Save(sol_ofs);
#else
x.Save(sol_ofs);
#endif
}
// 17. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
#ifdef COMPLEX_VERSION
sol_sock << "solution\n" << *pmesh << x.real() << flush;
#else
sol_sock << "solution\n" << *pmesh << x << flush;
#endif
}
// 18. Free the used memory.
delete a;
delete sigma;
delete muinv;
delete b;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
void E_exact(const Vector &x, Vector &E)
{
if (dim == 3)
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(2));
E(2) = sin(kappa * x(0));
}
else
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(0));
if (x.Size() == 3) { E(2) = 0.0; }
}
}
void curlE_exact(const Vector &x, Vector &curl)
{
if (dim == 3)
{
curl(0) = kappa * cos(kappa * x(2));
curl(1) = kappa * cos(kappa * x(0));
curl(2) = kappa * cos(kappa * x(1));
}
else
{
MFEM_VERIFY(false, "");
}
}
void f_exact(const Vector &x, Vector &f)
{
if (dim == 3)
{
// indefinite -m, definite +m
const double c = kappa * kappa;
#ifdef INDEFINITE
const double m = -omega * omega;
#else
const double m = omega * omega;
#endif
f(0) = (c + m) * sin(kappa * x(1));
f(1) = (c + m) * sin(kappa * x(2));
f(2) = (c + m) * sin(kappa * x(0));
}
else
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
if (x.Size() == 3) { f(2) = 0.0; }
}
}
-1
View File
@@ -13,7 +13,6 @@ set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
bilininteg_br2.cpp
bilininteg_convection_pa.cpp
bilininteg_convection_ea.cpp
bilininteg_dgtrace_pa.cpp
+2 -34
View File
@@ -1437,9 +1437,9 @@ void MixedBilinearForm::Assemble (int skip_zeros)
ftr = mesh->GetBdrFaceTransformations(i);
if (ftr)
{
trial_fes->GetFaceVDofs(ftr->ElementNo, tr_vdofs);
trial_fes->GetFaceVDofs(i, tr_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
trial_face_fe = trial_fes->GetFaceElement(ftr->ElementNo);
trial_face_fe = trial_fes->GetFaceElement(i);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
@@ -1770,41 +1770,9 @@ MixedBilinearForm::~MixedBilinearForm()
delete ext;
}
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
{
if (ext)
{
MFEM_ABORT("the assembly level has already been set!");
}
assembly = assembly_level;
switch (assembly)
{
case AssemblyLevel::LEGACYFULL:
case AssemblyLevel::FULL:
// Use the original implementation for now
break;
case AssemblyLevel::ELEMENT:
mfem_error("Element assembly not supported yet... stay tuned!");
break;
case AssemblyLevel::PARTIAL:
ext = new PADiscreteLinearOperatorExtension(this);
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
break;
default:
mfem_error("Unknown assembly level");
}
}
void DiscreteLinearOperator::Assemble(int skip_zeros)
{
if (ext)
{
ext->Assemble();
return;
}
Array<int> dom_vdofs, ran_vdofs;
ElementTransformation *T;
const FiniteElement *dom_fe, *ran_fe;
-16
View File
@@ -376,13 +376,6 @@ public:
/// Get the output finite element space prolongation matrix
virtual const Operator *GetOutputProlongation() const
{ return GetProlongation(); }
/** @brief Returns the output fe space restriction matrix, transposed
Logically, this is the transpose of GetOutputRestriction, but in
practice it is convenient to have it in transposed form for
construction of RAP operators in matrix-free methods. */
virtual const Operator *GetOutputRestrictionTranspose() const
{ return GetOutputProlongation(); }
/// Get the output finite element space restriction matrix
virtual const Operator *GetOutputRestriction() const
{ return GetRestriction(); }
@@ -984,18 +977,9 @@ public:
/// Access all interpolators added with AddDomainInterpolator().
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
/** @brief Construct the internal matrix representation of the discrete
linear operator. */
virtual void Assemble(int skip_zeros = 1);
/** @brief Get the output finite element space restriction matrix in
transposed form. */
virtual const Operator *GetOutputRestrictionTranspose() const
{ return test_fes->GetRestrictionTransposeOperator(); }
};
}
+1 -130
View File
@@ -1021,6 +1021,7 @@ void PAMixedBilinearFormExtension::Update()
localTrial.UseDevice(true);
localTrial.SetSize(elem_restrict_trial->Height(),
Device::GetMemoryType());
}
if (elem_restrict_test)
{
@@ -1220,134 +1221,4 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
}
}
PADiscreteLinearOperatorExtension::PADiscreteLinearOperatorExtension(
DiscreteLinearOperator *linop) :
PAMixedBilinearFormExtension(linop)
{
}
const
Operator *PADiscreteLinearOperatorExtension::GetOutputRestrictionTranspose()
const
{
return a->GetOutputRestrictionTranspose();
}
void PADiscreteLinearOperatorExtension::Assemble()
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssemblePA(*trialFes, *testFes);
}
test_multiplicity.UseDevice(true);
test_multiplicity.SetSize(elem_restrict_test->Width()); // l-vector
Vector ones(elem_restrict_test->Height()); // e-vector
ones = 1.0;
const ElementRestriction* elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (elem_restrict)
{
elem_restrict->MultTransposeUnsigned(ones, test_multiplicity);
}
else
{
mfem_error("A real ElementRestriction is required in this setting!");
}
auto tm = test_multiplicity.ReadWrite();
MFEM_FORALL(i, test_multiplicity.Size(),
{
tm[i] = 1.0 / tm[i];
});
}
void PADiscreteLinearOperatorExtension::AddMult(
const Vector &x, Vector &y, const double c) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
// * G operation
SetupMultInputs(elem_restrict_trial, x, localTrial,
elem_restrict_test, y, localTest, c);
// * B^TDB operation
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(localTrial, localTest);
}
// do a kind of "set" rather than "add" in the below
// operation as compared to the BilinearForm case
// * G^T operation (kind of...)
const ElementRestriction* elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (elem_restrict)
{
tempY.SetSize(y.Size());
elem_restrict->MultLeftInverse(localTest, tempY);
y += tempY;
}
else
{
mfem_error("In this setting you need a real ElementRestriction!");
}
}
void PADiscreteLinearOperatorExtension::AddMultTranspose(
const Vector &x, Vector &y, const double c) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
// do a kind of "set" rather than "add" in the below
// operation as compared to the BilinearForm case
// * G operation (kinda)
Vector xscaled(x);
MFEM_VERIFY(x.Size() == test_multiplicity.Size(), "Input vector of wrong size");
auto xs = xscaled.ReadWrite();
auto tm = test_multiplicity.Read();
MFEM_FORALL(i, x.Size(),
{
xs[i] *= tm[i];
});
SetupMultInputs(elem_restrict_test, xscaled, localTest,
elem_restrict_trial, y, localTrial, c);
// * B^TD^TB operation
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultTransposePA(localTest, localTrial);
}
// * G^T operation
if (elem_restrict_trial)
{
tempY.SetSize(y.Size());
elem_restrict_trial->MultTranspose(localTrial, tempY);
y += tempY;
}
else
{
mfem_error("Trial ElementRestriction not defined");
}
}
void PADiscreteLinearOperatorExtension::FormRectangularSystemOperator(
const Array<int>& ess1, const Array<int>& ess2, OperatorHandle &A)
{
const Operator *Pi = this->GetProlongation();
const Operator *RoT = this->GetOutputRestrictionTranspose();
Operator *rap = SetupRAP(Pi, RoT);
RectangularConstrainedOperator *Arco
= new RectangularConstrainedOperator(rap, ess1, ess2, rap != this);
A.Reset(Arco);
}
} // namespace mfem
+1 -31
View File
@@ -21,7 +21,6 @@ namespace mfem
class BilinearForm;
class MixedBilinearForm;
class DiscreteLinearOperator;
/// Class extending the BilinearForm class to support different AssemblyLevels.
/** FA - Full Assembly
@@ -213,7 +212,7 @@ protected:
mutable Vector localTrial, localTest, tempY;
const Operator *elem_restrict_trial; // Not owned
const Operator *elem_restrict_test; // Not owned
private:
/// Helper function to set up inputs/outputs for Mult or MultTranspose
void SetupMultInputs(const Operator *elem_restrict_x,
const Vector &x, Vector &localX,
@@ -259,35 +258,6 @@ public:
void Update();
};
/**
@brief Partial assembly extension for DiscreteLinearOperator
This acts very much like PAMixedBilinearFormExtension, but its
FormRectangularSystemOperator implementation emulates 'Set' rather than
'Add' in the assembly case.
*/
class PADiscreteLinearOperatorExtension : public PAMixedBilinearFormExtension
{
public:
PADiscreteLinearOperatorExtension(DiscreteLinearOperator *linop);
/// Partial assembly of all internal integrators
void Assemble();
void AddMult(const Vector &x, Vector &y, const double c) const;
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
void FormRectangularSystemOperator(const Array<int>&, const Array<int>&,
OperatorHandle& A);
const Operator * GetOutputRestrictionTranspose() const;
private:
Vector test_multiplicity;
};
}
#endif
+15 -56
View File
@@ -22,14 +22,14 @@ namespace mfem
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssemblePA(fes)\n"
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssemblePA(fes, fes)\n"
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
" is not implemented for this class.");
}
@@ -92,7 +92,7 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
{
mfem_error ("BilinearFormIntegrator::AddMultTransposePA(...)\n"
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose(...)\n"
" is not implemented for this class.");
}
@@ -1913,12 +1913,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix(
double w;
#ifdef MFEM_THREAD_SAFE
Vector D(DQ ? DQ->GetVDim() : 0);
Vector D(VQ ? VQ->GetVDim() : 0);
DenseMatrix trial_vshape(dof, spaceDim);
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#else
trial_vshape.SetSize(dof, spaceDim);
D.SetSize(DQ ? DQ->GetVDim() : 0);
D.SetSize(VQ ? VQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
DenseMatrix tmp(trial_vshape.Height(), K.Width());
@@ -1950,9 +1950,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix(
Mult(trial_vshape,K,tmp);
AddMultABt(tmp,trial_vshape,elmat);
}
else if (DQ)
else if (VQ)
{
DQ->Eval(D, Trans, ip);
VQ->Eval(D, Trans, ip);
D *= w;
AddMultADAt(trial_vshape, D, elmat);
}
@@ -1984,12 +1984,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
#ifdef MFEM_THREAD_SAFE
DenseMatrix trial_vshape(trial_dof, spaceDim);
Vector shape(test_dof);
Vector D(DQ ? DQ->GetVDim() : 0);
Vector D(VQ ? VQ->GetVDim() : 0);
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#else
trial_vshape.SetSize(trial_dof, spaceDim);
shape.SetSize(test_dof);
D.SetSize(DQ ? DQ->GetVDim() : 0);
D.SetSize(VQ ? VQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
@@ -2013,9 +2013,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
test_fe.CalcShape(ip, shape);
w = ip.weight * Trans.Weight();
if (DQ)
if (VQ)
{
DQ->Eval(D, Trans, ip);
VQ->Eval(D, Trans, ip);
D *= w;
for (int d = 0; d < vdim; d++)
{
@@ -2081,12 +2081,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
#ifdef MFEM_THREAD_SAFE
DenseMatrix trial_vshape(trial_dof,spaceDim);
DenseMatrix test_vshape(test_dof,spaceDim);
Vector D(DQ ? DQ->GetVDim() : 0);
Vector D(VQ ? VQ->GetVDim() : 0);
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#else
trial_vshape.SetSize(trial_dof,spaceDim);
test_vshape.SetSize(test_dof,spaceDim);
D.SetSize(DQ ? DQ->GetVDim() : 0);
D.SetSize(VQ ? VQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
DenseMatrix tmp(test_vshape.Height(), K.Width());
@@ -2118,9 +2118,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
Mult(test_vshape,K,tmp);
AddMultABt(tmp,trial_vshape,elmat);
}
else if (DQ)
else if (VQ)
{
DQ->Eval(D, Trans, ip);
VQ->Eval(D, Trans, ip);
D *= w;
AddMultADBt(test_vshape,D,trial_vshape,elmat);
}
@@ -3515,47 +3515,6 @@ VectorScalarProductInterpolator::AssembleElementMatrix2(
}
void
ScalarCrossProductInterpolator::AssembleElementMatrix2(
const FiniteElement &dom_fe,
const FiniteElement &ran_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
// Vector coefficient product with vector shape functions
struct VCrossVShapeCoefficient : public VectorCoefficient
{
VectorCoefficient &VQ;
const FiniteElement &fe;
DenseMatrix vshape;
Vector vc;
VCrossVShapeCoefficient(VectorCoefficient &vq, const FiniteElement &fe_)
: VectorCoefficient(fe_.GetDof()), VQ(vq), fe(fe_),
vshape(vdim, vq.GetVDim()), vc(vq.GetVDim()) { }
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
VQ.Eval(vc, T, ip);
fe.CalcPhysVShape(T, vshape);
for (int k = 0; k < vdim; k++)
{
V(k) = vc(0) * vshape(k,1) - vc(1) * vshape(k,0);
}
}
};
VCrossVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
Vector elmat_as_vec(elmat.Data(), elmat.Height()*elmat.Width());
ran_fe.Project(dom_shape_coeff, Trans, elmat_as_vec);
}
void
VectorCrossProductInterpolator::AssembleElementMatrix2(
const FiniteElement &dom_fe,
+37 -179
View File
@@ -465,8 +465,8 @@ protected:
: same_calc_shape(false), Q(NULL), VQ(NULL), DQ(NULL), MQ(NULL) {}
MixedVectorIntegrator(Coefficient &q)
: same_calc_shape(false), Q(&q), VQ(NULL), DQ(NULL), MQ(NULL) {}
MixedVectorIntegrator(VectorCoefficient &vq, bool diag = true)
: same_calc_shape(false), Q(NULL), VQ(diag?NULL:&vq), DQ(diag?&vq:NULL),
MixedVectorIntegrator(VectorCoefficient &dq, bool diag = true)
: same_calc_shape(false), Q(NULL), VQ(diag?NULL:&dq), DQ(diag?&dq:NULL),
MQ(NULL) {}
MixedVectorIntegrator(MatrixCoefficient &mq)
: same_calc_shape(false), Q(NULL), VQ(NULL), DQ(NULL), MQ(&mq) {}
@@ -503,7 +503,7 @@ protected:
Coefficient *Q;
VectorCoefficient *VQ;
DiagonalMatrixCoefficient *DQ;
VectorCoefficient *DQ;
MatrixCoefficient *MQ;
private:
@@ -901,7 +901,7 @@ public:
MixedVectorMassIntegrator() { same_calc_shape = true; }
MixedVectorMassIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) { same_calc_shape = true; }
MixedVectorMassIntegrator(DiagonalMatrixCoefficient &dq)
MixedVectorMassIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
MixedVectorMassIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
@@ -1019,7 +1019,7 @@ public:
MixedGradGradIntegrator() { same_calc_shape = true; }
MixedGradGradIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) { same_calc_shape = true; }
MixedGradGradIntegrator(DiagonalMatrixCoefficient &dq)
MixedGradGradIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
MixedGradGradIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
@@ -1107,7 +1107,7 @@ public:
MixedCurlCurlIntegrator() { same_calc_shape = true; }
MixedCurlCurlIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) { same_calc_shape = true; }
MixedCurlCurlIntegrator(DiagonalMatrixCoefficient &dq)
MixedCurlCurlIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
MixedCurlCurlIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
@@ -1651,7 +1651,7 @@ public:
MixedVectorGradientIntegrator() {}
MixedVectorGradientIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorGradientIntegrator(DiagonalMatrixCoefficient &dq)
MixedVectorGradientIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorGradientIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1705,7 +1705,7 @@ public:
MixedVectorCurlIntegrator() {}
MixedVectorCurlIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorCurlIntegrator(DiagonalMatrixCoefficient &dq)
MixedVectorCurlIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorCurlIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1760,7 +1760,7 @@ public:
MixedVectorWeakCurlIntegrator() {}
MixedVectorWeakCurlIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorWeakCurlIntegrator(DiagonalMatrixCoefficient &dq)
MixedVectorWeakCurlIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorWeakCurlIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1813,7 +1813,7 @@ public:
MixedVectorWeakDivergenceIntegrator() {}
MixedVectorWeakDivergenceIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorWeakDivergenceIntegrator(DiagonalMatrixCoefficient &dq)
MixedVectorWeakDivergenceIntegrator(VectorCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorWeakDivergenceIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1844,10 +1844,8 @@ protected:
};
/** Class for integrating the bilinear form a(u,v) := (Q grad u, v) where Q is a
scalar coefficient, and v is a vector with components v_i in the same (H1) space
as u.
See also MixedVectorGradientIntegrator when v is in H(curl). */
scalar coefficient, and v is a vector with components v_i in the same space
as u. */
class GradientIntegrator : public BilinearFormIntegrator
{
protected:
@@ -1902,7 +1900,6 @@ protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
SymmetricMatrixCoefficient *SMQ;
private:
Vector vec, pointflux, shape;
@@ -1925,28 +1922,19 @@ private:
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator()
: Q(NULL), VQ(NULL), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
: Q(NULL), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a scalar coefficient q
DiffusionIntegrator(Coefficient &q)
: Q(&q), VQ(NULL), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
: Q(&q), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a vector coefficient q
DiffusionIntegrator(VectorCoefficient &q)
: Q(NULL), VQ(&q), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
: Q(NULL), VQ(&q), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a matrix coefficient q
DiffusionIntegrator(MatrixCoefficient &q)
: Q(NULL), VQ(NULL), MQ(&q), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a symmetric matrix coefficient q
DiffusionIntegrator(SymmetricMatrixCoefficient &q)
: Q(NULL), VQ(NULL), MQ(NULL), SMQ(&q), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
: Q(NULL), VQ(NULL), MQ(&q), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
virtual ~DiffusionIntegrator()
{
@@ -2345,9 +2333,8 @@ private:
protected:
Coefficient *Q;
DiagonalMatrixCoefficient *DQ;
VectorCoefficient *DQ;
MatrixCoefficient *MQ;
SymmetricMatrixCoefficient *SMQ;
// PA extension
Vector pa_data;
@@ -2358,18 +2345,14 @@ protected:
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
public:
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; SMQ = NULL; }
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; }
/// Construct a bilinear form integrator for Nedelec elements
CurlCurlIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(&q), DQ(NULL), MQ(NULL), SMQ(NULL) { }
CurlCurlIntegrator(DiagonalMatrixCoefficient &dq,
const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(&dq), MQ(NULL), SMQ(NULL) { }
BilinearFormIntegrator(ir), Q(&q) { DQ = NULL; MQ = NULL; }
CurlCurlIntegrator(VectorCoefficient &dq, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), DQ(&dq) { Q = NULL; MQ = NULL; }
CurlCurlIntegrator(MatrixCoefficient &mq, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(&mq), SMQ(NULL) { }
CurlCurlIntegrator(SymmetricMatrixCoefficient &smq,
const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(NULL), SMQ(&smq) { }
BilinearFormIntegrator(ir), MQ(&mq) { Q = NULL; DQ = NULL; }
/* Given a particular Finite Element, compute the
element curl-curl matrix elmat */
@@ -2426,9 +2409,8 @@ public:
class VectorFEMassIntegrator: public BilinearFormIntegrator
{
private:
void Init(Coefficient *q, DiagonalMatrixCoefficient *dq, MatrixCoefficient *mq,
SymmetricMatrixCoefficient *smq)
{ Q = q; DQ = dq; MQ = mq; SMQ = smq; }
void Init(Coefficient *q, VectorCoefficient *vq, MatrixCoefficient *mq)
{ Q = q; VQ = vq; MQ = mq; }
#ifndef MFEM_THREAD_SAFE
Vector shape;
@@ -2441,9 +2423,8 @@ private:
protected:
Coefficient *Q;
DiagonalMatrixCoefficient *DQ;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
SymmetricMatrixCoefficient *SMQ;
// PA extension
Vector pa_data;
@@ -2456,15 +2437,13 @@ protected:
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
public:
VectorFEMassIntegrator() { Init(NULL, NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient &q) { Init(&q, NULL, NULL, NULL); }
VectorFEMassIntegrator(DiagonalMatrixCoefficient *_dq) { Init(NULL, _dq, NULL, NULL); }
VectorFEMassIntegrator(DiagonalMatrixCoefficient &dq) { Init(NULL, &dq, NULL, NULL); }
VectorFEMassIntegrator(MatrixCoefficient *_mq) { Init(NULL, NULL, _mq, NULL); }
VectorFEMassIntegrator(MatrixCoefficient &mq) { Init(NULL, NULL, &mq, NULL); }
VectorFEMassIntegrator(SymmetricMatrixCoefficient &smq) { Init(NULL, NULL, NULL, &smq); }
VectorFEMassIntegrator(SymmetricMatrixCoefficient *smq) { Init(NULL, NULL, NULL, smq); }
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL); }
VectorFEMassIntegrator(Coefficient &q) { Init(&q, NULL, NULL); }
VectorFEMassIntegrator(VectorCoefficient *_vq) { Init(NULL, _vq, NULL); }
VectorFEMassIntegrator(VectorCoefficient &vq) { Init(NULL, &vq, NULL); }
VectorFEMassIntegrator(MatrixCoefficient *_mq) { Init(NULL, NULL, _mq); }
VectorFEMassIntegrator(MatrixCoefficient &mq) { Init(NULL, NULL, &mq); }
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -2673,28 +2652,11 @@ public:
/** Integrator for the DG form:
alpha < rho_u (u.n) {v},[w] > + beta < rho_u |u.n| [v],[w] >,
where v and w are the trial and test variables, respectively, and rho/u are
given scalar/vector coefficients. {v} represents the average value of v on
the face and [v] is the jump such that {v}=(v1+v2)/2 and [v]=(v1-v2) for the
face between elements 1 and 2. For boundary elements, v2=0. The vector
coefficient, u, is assumed to be continuous across the faces and when given
the scalar coefficient, rho, is assumed to be discontinuous. The integrator
uses the upwind value of rho, rho_u, which is value from the side into which
the vector coefficient, u, points.
One use case for this integrator is to discretize the operator -u.grad(v)
with a DG formulation. The resulting formulation uses the
ConvectionIntegrator (with coefficient u, and parameter alpha = -1) and the
transpose of the DGTraceIntegrator (with coefficient u, and parameters
alpha = 1, beta = -1/2 to use the upwind face flux). This discretization and
the handling of the inflow and outflow boundaries is illustrated in Example
9/9p.
Another use case for this integrator is to discretize the operator -div(u v)
with a DG formulation. The resulting formulation is conservative and
consists of the transpose of the ConvectionIntegrator (with coefficient u,
and parameter alpha = 1) plus the DGTraceIntegrator (with coefficient u, and
parameters alpha = -1, beta = -1/2 to use the upwind face flux).
*/
given scalar/vector coefficients. The vector coefficient, u, is assumed to
be continuous across the faces and when given the scalar coefficient, rho,
is assumed to be discontinuous. The integrator uses the upwind value of rho,
rho_u, which is value from the side into which the vector coefficient, u,
points. */
class DGTraceIntegrator : public BilinearFormIntegrator
{
protected:
@@ -2788,51 +2750,6 @@ public:
DenseMatrix &elmat);
};
/** Integrator for the "BR2" diffusion stabilization term
sum_e eta (r_e([u]), r_e([v]))
where r_e is the lifting operator defined on each edge e. The parameter eta
can be chosen to be one to obtain a stable discretization. The constructor
for this integrator requires the finite element space because the lifting
operator depends on the element-wise inverse mass matrix.
BR2 stands for the second method of Bassi and Rebay:
- F. Bassi and S. Rebay. A high order discontinuous Galerkin method for
compressible turbulent flows. In B. Cockburn, G. E. Karniadakis, and
C.-W. Shu, editors, Discontinuous Galerkin Methods, pages 7788. Springer
Berlin Heidelberg, 2000.
- D. N. Arnold, F. Brezzi, B. Cockburn, and L. D. Marini. Unified analysis
of discontinuous Galerkin methods for elliptic problems. SIAM Journal on
Numerical Analysis, 39(5):17491779, 2002.
*/
class DGDiffusionBR2Integrator : public BilinearFormIntegrator
{
protected:
double eta;
// Block factorizations of local mass matrices, with offsets for the case of
// not equally sized blocks (mixed meshes, p-refinement)
Array<double> Minv;
Array<int> ipiv;
Array<int> ipiv_offsets, Minv_offsets;
Vector shape1, shape2;
DenseMatrix R11, R12, R21, R22;
DenseMatrix MinvR11, MinvR12, MinvR21, MinvR22;
DenseMatrix Re, MinvRe;
public:
DGDiffusionBR2Integrator(class FiniteElementSpace *fes, double e = 1.0);
using BilinearFormIntegrator::AssembleFaceMatrix;
virtual void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** Integrator for the DG elasticity form, for the formulations see:
- PhD Thesis of Jonas De Basabe, High-Order Finite %Element Methods for
Seismic Wave Propagation, UT Austin, 2009, p. 23, and references therein
@@ -2991,36 +2908,11 @@ class DiscreteInterpolator : public BilinearFormIntegrator { };
class GradientInterpolator : public DiscreteInterpolator
{
public:
GradientInterpolator() : dofquad_fe(NULL) { }
virtual ~GradientInterpolator() { delete dofquad_fe; }
virtual void AssembleElementMatrix2(const FiniteElement &h1_fe,
const FiniteElement &nd_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{ nd_fe.ProjectGrad(h1_fe, Trans, elmat); }
using BilinearFormIntegrator::AssemblePA;
/** @brief Setup method for PA data.
@param[in] trial_fes H1 Lagrange space
@param[in] test_fes H(curl) Nedelec space
*/
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
private:
/// 1D finite element that generates and owns the 1D DofToQuad maps below
FiniteElement * dofquad_fe;
bool B_id; // is the B basis operator (maps_C_C) the identity?
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
int dim, ne, o_dofs1D, c_dofs1D;
};
@@ -3035,24 +2927,6 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat)
{ ran_fe.Project(dom_fe, Trans, elmat); }
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
private:
/// 1D finite element that generates and owns the 1D DofToQuad maps below
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
int dim, ne, o_dofs1D, c_dofs1D;
Vector pa_data;
};
@@ -3152,22 +3026,6 @@ protected:
VectorCoefficient *VQ;
};
/** Interpolator of the 2D cross product between a vector coefficient and an
H(curl)-conforming field onto an L2-conforming field. */
class ScalarCrossProductInterpolator : public DiscreteInterpolator
{
public:
ScalarCrossProductInterpolator(VectorCoefficient & vc)
: VQ(&vc) { }
virtual void AssembleElementMatrix2(const FiniteElement &nd_fe,
const FiniteElement &l2_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
protected:
VectorCoefficient *VQ;
};
/** Interpolator of the cross product between a vector coefficient and an
H(curl)-conforming field onto an H(div)-conforming field. The range space
can also be vector L2. */
-242
View File
@@ -1,242 +0,0 @@
// Copyright (c) 2010-2020, 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 "bilininteg.hpp"
#include "pfespace.hpp"
#include <algorithm>
namespace mfem
{
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
double e) : eta(e)
{
// Precompute local mass matrix inverses needed for the lifting operators
// First compute offsets and total size needed (e.g. for mixed meshes or
// p-refinement)
int nel = fes->GetNE();
Minv_offsets.SetSize(nel+1);
ipiv_offsets.SetSize(nel+1);
ipiv_offsets[0] = 0;
Minv_offsets[0] = 0;
for (int i=0; i<nel; ++i)
{
int dof = fes->GetFE(i)->GetDof();
ipiv_offsets[i+1] = ipiv_offsets[i] + dof;
Minv_offsets[i+1] = Minv_offsets[i] + dof*dof;
}
#ifdef MFEM_USE_MPI
// When running in parallel, we also need to compute the local mass matrices
// of face neighbor elements
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace *>(fes);
if (pfes != NULL)
{
ParMesh *pmesh = pfes->GetParMesh();
pfes->ExchangeFaceNbrData();
int nel_nbr = pmesh->GetNFaceNeighborElements();
Minv_offsets.SetSize(nel+nel_nbr+1);
ipiv_offsets.SetSize(nel+nel_nbr+1);
for (int i=0; i<nel_nbr; ++i)
{
int dof = pfes->GetFaceNbrFE(i)->GetDof();
ipiv_offsets[nel+i+1] = ipiv_offsets[nel+i] + dof;
Minv_offsets[nel+i+1] = Minv_offsets[nel+i] + dof*dof;
}
nel += nel_nbr;
}
#endif
// The final "offset" is the total size of all the blocks
Minv.SetSize(Minv_offsets[nel]);
ipiv.SetSize(ipiv_offsets[nel]);
// Assemble the local mass matrices and compute LU factorization
MassIntegrator mi;
for (int i=0; i<nel; ++i)
{
const FiniteElement *fe = NULL;
ElementTransformation *tr = NULL;
if (i < fes->GetNE())
{
fe = fes->GetFE(i);
tr = fes->GetElementTransformation(i);
}
else
{
#ifdef MFEM_USE_MPI
int inbr = i - fes->GetNE();
fe = pfes->GetFaceNbrFE(inbr);
tr = pfes->GetParMesh()->GetFaceNbrElementTransformation(inbr);
#endif
}
int dof = fe->GetDof();
double *Minv_el = &Minv[Minv_offsets[i]];
int *ipiv_el = &ipiv[ipiv_offsets[i]];
DenseMatrix Me(Minv_el, dof, dof);
mi.AssembleElementMatrix(*fe, *tr, Me);
LUFactors lu(Minv_el, ipiv_el);
lu.Factor(dof);
}
}
void DGDiffusionBR2Integrator::AssembleFaceMatrix(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
int ndof1 = el1.GetDof();
shape1.SetSize(ndof1);
R11.SetSize(ndof1, ndof1);
R11 = 0.0;
LUFactors M1inv(&Minv[Minv_offsets[Trans.Elem1No]],
&ipiv[ipiv_offsets[Trans.Elem1No]]);
LUFactors M2inv;
double factor = Geometries.NumBdr(Trans.Elem1->GetGeometryType());
int ndof2;
if (Trans.Elem2No >= 0)
{
ndof2 = el2.GetDof();
shape2.SetSize(ndof2);
R12.SetSize(ndof1, ndof2);
R21.SetSize(ndof2, ndof1);
R22.SetSize(ndof2, ndof2);
M2inv.data = &Minv[Minv_offsets[Trans.Elem2No]];
M2inv.ipiv = &ipiv[ipiv_offsets[Trans.Elem2No]];
R12 = 0.0;
R21 = 0.0;
R22 = 0.0;
Geometry::Type geom2 = Trans.Elem2->GetGeometryType();
factor = std::max(factor, double(Geometries.NumBdr(geom2)));
}
else
{
ndof2 = 0;
}
int ndofs = ndof1 + ndof2;
Re.SetSize(ndofs, ndofs);
MinvRe.SetSize(ndofs, ndofs);
elmat.SetSize(ndofs);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order;
if (ndof2)
{
order = 2*std::max(el1.GetOrder(), el2.GetOrder());
}
else
{
order = 2*el1.GetOrder();
}
ir = &IntRules.Get(Trans.FaceGeom, order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
IntegrationPoint eip1, eip2;
Trans.Loc1.Transform(ip, eip1);
el1.CalcShape(eip1, shape1);
if (ndof2)
{
Trans.Loc2.Transform(ip, eip2);
el2.CalcShape(eip2, shape2);
}
double w = factor*sqrt(eta)*ip.weight*Trans.Face->Weight();
if (ndof2)
{
w /= 2;
}
for (int i = 0; i < ndof1; i++)
{
const double wsi = w*shape1(i);
for (int j = 0; j < ndof1; j++)
{
R11(i, j) += wsi*shape1(j);
}
}
if (ndof2)
{
for (int i = 0; i < ndof2; i++)
{
const double wsi = w*shape2(i);
for (int j = 0; j < ndof1; j++)
{
R21(i, j) += wsi*shape1(j);
R12(j, i) -= wsi*shape1(j);
}
for (int j = 0; j < ndof2; j++)
{
R22(i, j) -= wsi*shape2(j);
}
}
}
}
MinvR11 = R11;
M1inv.Solve(ndof1, ndof1, MinvR11.Data());
for (int i = 0; i < ndof1; i++)
{
for (int j = 0; j < ndof1; j++)
{
Re(i, j) = R11(i, j);
MinvRe(i, j) = MinvR11(i, j);
}
}
if (ndof2)
{
MinvR12 = R12;
MinvR21 = R21;
MinvR22 = R22;
M1inv.Solve(ndof1, ndof2, MinvR12.Data());
M2inv.Solve(ndof2, ndof1, MinvR21.Data());
M2inv.Solve(ndof2, ndof2, MinvR22.Data());
for (int i = 0; i < ndof2; i++)
{
for (int j = 0; j < ndof1; j++)
{
Re(ndof1 + i, j) = R21(i, j);
MinvRe(ndof1 + i, j) = MinvR21(i, j);
Re(j, ndof1 + i) = R12(j, i);
MinvRe(j, ndof1 + i) = MinvR12(j, i);
}
for (int j = 0; j < ndof2; j++)
{
Re(ndof1 + i, ndof1 + j) = R22(i, j);
MinvRe(ndof1 + i, ndof1 + j) = MinvR22(i, j);
}
}
}
// Compute the matrix associated with (r_e([u]), r_e([u])).
// The matrix for r_e([u]) is `MinvRe`, and so we need to form the product
// `(MinvRe)^T M MinvRe`. Using `Minv^T M = Minv M = I`, we obtain
// `Re^T MinvRe`.
MultAtB(Re, MinvRe, elmat);
}
}
+30 -33
View File
@@ -379,54 +379,51 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
const int MQfullDim = MQ ? MQ->GetHeight() * MQ->GetWidth() : 0;
if (MQ)
{
symmetric = false;
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
const int MQsymmDim = MQ->GetWidth() * (MQ->GetWidth() + 1) / 2;
coeffDim = MQfullDim;
const int MQdim = MQ->IsSymmetric() ? MQsymmDim : MQfullDim;
coeffDim = MQdim;
coeff.SetSize(MQfullDim * nq * ne);
coeff.SetSize(MQdim * nq * ne);
symmetric = MQ ? MQ->IsSymmetric() : true;
DenseMatrix M;
M.SetSize(dim);
auto C = Reshape(coeff.HostWrite(), MQfullDim, nq, ne);
for (int e=0; e<ne; ++e)
Vector Msymm;
if (symmetric)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
C(j+(i*dim), p, e) = M(i,j);
}
}
Msymm.SetSize(MQsymmDim);
}
else
{
M.SetSize(dim);
}
}
else if (SMQ)
{
MFEM_VERIFY(SMQ->GetSize() == dim, "");
coeffDim = symmDims;
coeff.SetSize(symmDims * nq * ne);
DenseSymmetricMatrix M;
M.SetSize(dim);
auto C = Reshape(coeff.HostWrite(), symmDims, nq, ne);
auto C = Reshape(coeff.HostWrite(), MQdim, nq, ne);
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
SMQ->Eval(M, *tr, ir->IntPoint(p));
int cnt = 0;
for (int i=0; i<dim; ++i)
for (int j=i; j<dim; ++j, ++cnt)
if (MQ->IsSymmetric())
{
MQ->EvalSymmetric(Msymm, *tr, ir->IntPoint(p));
for (int i=0; i<MQsymmDim; ++i)
{
C(cnt, p, e) = M(i,j);
C(i, p, e) = Msymm[i];
}
}
else
{
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
C(j+(i*dim), p, e) = M(i,j);
}
}
}
}
}
+1 -1
View File
@@ -334,7 +334,7 @@ static void PAGradientApplyTranspose2D(const int NE,
const int q1d = 0)
{
// TODO
MFEM_ASSERT(false, "PAGradientApplyTranspose2D not implemented.");
MFEM_ASSERT(false, "GradientPAApplyTranspose 3D not implemented.");
}
// PA Gradient Apply 3D kernel
+37 -1952
View File
File diff suppressed because it is too large Load Diff
+40 -33
View File
@@ -761,12 +761,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
trial_fetype = trial_el->GetDerivType();
test_fetype = test_el->GetDerivType();
const int MQsymmDim = SMQ ? (SMQ->GetSize() * (SMQ->GetSize() + 1)) / 2 : 0;
const int MQsymmDim = MQ ? (MQ->GetWidth() * (MQ->GetWidth() + 1)) / 2 : 0;
const int MQfullDim = MQ ? (MQ->GetHeight() * MQ->GetWidth()) : 0;
const int MQdim = MQ ? MQfullDim : MQsymmDim;
const int coeffDim = (MQ || SMQ) ? MQdim : (DQ ? DQ->GetVDim() : 1);
const int MQdim = MQ ? (MQ->IsSymmetric() ? MQsymmDim : MQfullDim) : 0;
const int coeffDim = MQ ? MQdim : (VQ ? VQ->GetVDim() : 1);
symmetric = (MQ == NULL);
symmetric = MQ ? MQ->IsSymmetric() : true;
const bool trial_curl = (trial_fetype == mfem::FiniteElement::CURL);
const bool trial_div = (trial_fetype == mfem::FiniteElement::DIV);
@@ -783,13 +783,24 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
Vector coeff(coeffDim * ne * nq);
coeff = 1.0;
auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
if (Q || DQ || MQ || SMQ)
if (Q || VQ || MQ)
{
Vector D(DQ ? coeffDim : 0);
Vector D(VQ ? coeffDim : 0);
DenseMatrix M;
DenseSymmetricMatrix SM;
Vector Msymm;
if (MQ)
{
if (symmetric)
{
Msymm.SetSize(MQsymmDim);
}
else
{
M.SetSize(dim);
}
}
if (DQ)
if (VQ)
{
MFEM_VERIFY(coeffDim == dim, "");
}
@@ -797,12 +808,6 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
{
MFEM_VERIFY(coeffDim == MQdim, "");
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
M.SetSize(dim);
}
if (SMQ)
{
MFEM_VERIFY(SMQ->GetSize() == dim, "");
SM.SetSize(dim);
}
for (int e=0; e<ne; ++e)
@@ -812,27 +817,29 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
{
if (MQ)
{
MQ->Eval(M, *tr, ir->IntPoint(p));
if (MQ->IsSymmetric())
{
MQ->EvalSymmetric(Msymm, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
for (int i=0; i<MQsymmDim; ++i)
{
coeffh(j+(i*dim), p, e) = M(i,j);
coeffh(i, p, e) = Msymm[i];
}
}
else
{
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
coeffh(j+(i*dim), p, e) = M(i,j);
}
}
}
else if (SMQ)
else if (VQ)
{
SMQ->Eval(SM, *tr, ir->IntPoint(p));
int cnt = 0;
for (int i=0; i<dim; ++i)
for (int j=i; j<dim; ++j, ++cnt)
{
coeffh(cnt, p, e) = SM(i,j);
}
}
else if (DQ)
{
DQ->Eval(D, *tr, ir->IntPoint(p));
VQ->Eval(D, *tr, ir->IntPoint(p));
for (int i=0; i<coeffDim; ++i)
{
coeffh(i, p, e) = D[i];
@@ -1000,14 +1007,14 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
else if (trial_curl && test_div)
{
const bool scalarCoeff = !(DQ || MQ || SMQ);
const bool scalarCoeff = !(VQ || MQ);
PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
true, mapsO->B, mapsC->B, mapsOtest->Bt,
mapsCtest->Bt, pa_data, x, y);
}
else if (trial_div && test_curl)
{
const bool scalarCoeff = !(DQ || MQ || SMQ);
const bool scalarCoeff = !(VQ || MQ);
PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
false, mapsO->B, mapsC->B, mapsOtest->Bt,
mapsCtest->Bt, pa_data, x, y);
@@ -1031,7 +1038,7 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
else if ((trial_curl && test_div) || (trial_div && test_curl))
{
const bool scalarCoeff = !(DQ || MQ || SMQ);
const bool scalarCoeff = !(VQ || MQ);
PAHcurlHdivMassApply2D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
trial_curl, mapsO->B, mapsC->B, mapsOtest->Bt,
mapsCtest->Bt, pa_data, x, y);
+1 -32
View File
@@ -301,7 +301,7 @@ void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
K.SetSize(height, width);
if (symmetric) // Use SymmFunction (deprecated version)
if (symmetric) // Use SymmFunction
{
MFEM_VERIFY(height == width && SymmFunction,
"MatrixFunctionCoefficient is not symmetric");
@@ -371,36 +371,6 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
}
}
void SymmetricMatrixFunctionCoefficient::Eval(DenseSymmetricMatrix &K,
ElementTransformation &T,
const IntegrationPoint &ip)
{
double x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
K.SetSize(dim);
if (Function)
{
Function(transip, K);
}
else if (TDFunction)
{
TDFunction(transip, GetTime(), K);
}
else
{
K = mat;
}
if (Q)
{
K *= Q->Eval(T, ip, GetTime());
}
}
MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
: MatrixCoefficient (dim)
{
@@ -625,7 +595,6 @@ void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
{
a->Eval(ma, T, ip);
b->Eval(vb, T, ip);
V.SetSize(vdim);
ma.Mult(vb, V);
}
+16 -121
View File
@@ -688,7 +688,6 @@ public:
const IntegrationRule &ir);
};
typedef VectorCoefficient DiagonalMatrixCoefficient;
/// Base class for Matrix Coefficients that optionally depend on time and space.
class MatrixCoefficient
@@ -696,7 +695,7 @@ class MatrixCoefficient
protected:
int height, width;
double time;
bool symmetric; // deprecated
bool symmetric;
public:
/// Construct a dim x dim matrix coefficient.
@@ -722,7 +721,6 @@ public:
/// For backward compatibility get the width of the matrix.
int GetVDim() const { return width; }
/** @deprecated Use SymmetricMatrixCoefficient instead */
bool IsSymmetric() const { return symmetric; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
@@ -733,13 +731,11 @@ public:
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) = 0;
/// (DEPRECATED) Evaluate a symmetric matrix coefficient.
/** @brief Evaluate the upper triangular entries of the matrix coefficient
in the symmetric case, similarly to Eval. Matrix entry (i,j) is stored
in K[j - i + os_i] for 0 <= i <= j < width, os_0 = 0,
os_{i+1} = os_i + width - i. That is, K = {M(0,0), ..., M(0,w-1),
M(1,1), ..., M(1,w-1), ..., M(w-1,w-1) with w = width.
@deprecated Use Eval() instead. */
M(1,1), ..., M(1,w-1), ..., M(w-1,w-1) with w = width. */
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip)
{ mfem_error("MatrixCoefficient::EvalSymmetric"); }
@@ -771,7 +767,7 @@ class MatrixFunctionCoefficient : public MatrixCoefficient
{
private:
std::function<void(const Vector &, DenseMatrix &)> Function;
std::function<void(const Vector &, Vector &)> SymmFunction; // deprecated
std::function<void(const Vector &, Vector &)> SymmFunction;
std::function<void(const Vector &, double, DenseMatrix &)> TDFunction;
Coefficient *Q;
@@ -795,18 +791,6 @@ public:
: MatrixCoefficient(m.Height(), m.Width()), Q(&q), mat(m)
{ }
/** @brief Define a time-independent symmetric square matrix coefficient from
a std function */
/** \param dim - the size of the matrix
\param SymmF - function used in EvalSymmetric
\param q - optional scalar Coefficient to scale the matrix coefficient
@deprecated Use another constructor without setting SymmFunction. */
MatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, Vector &)> SymmF,
Coefficient *q = NULL)
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
{ }
/// Define a time-dependent square matrix coefficient from a std function
/** \param dim - the size of the matrix
\param TDF - time-dependent function
@@ -817,12 +801,22 @@ public:
: MatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/** @brief Define a time-independent symmetric square matrix coefficient from
a std function */
/** \param dim - the size of the matrix
\param SymmF - function used in EvalSymmetric
\param q - optional scalar Coefficient to scale the matrix coefficient */
MatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, Vector &)> SymmF,
Coefficient *q = NULL)
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
{ }
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
/// (DEPRECATED) Evaluate the symmetric matrix coefficient at @a ip.
/** @deprecated Use Eval() instead. */
/// Evaluate the symmetric matrix coefficient at @a ip.
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -830,6 +824,7 @@ public:
};
/** @brief Matrix coefficient defined by a matrix of scalar coefficients.
Coefficients that are not set will evaluate to zero in the vector. The
coefficient is stored as a flat Array with indexing (i,j) -> i*width+j. */
@@ -945,106 +940,6 @@ public:
}
};
/// Base class for symmetric matrix coefficients that optionally depend on time and space.
class SymmetricMatrixCoefficient
{
protected:
int dim;
double time;
public:
/// Construct a dim x dim matrix coefficient.
explicit SymmetricMatrixCoefficient(int dimension)
{ dim = dimension; time = 0.; }
/// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
/// Get the time for time dependent coefficients
double GetTime() { return time; }
/// Get the size of the matrix.
int GetSize() const { return dim; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
at the point @a ip, storing the result in @a K. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) = 0;
virtual ~SymmetricMatrixCoefficient() { }
};
/// A matrix coefficient that is constant in space and time.
class SymmetricMatrixConstantCoefficient : public SymmetricMatrixCoefficient
{
private:
DenseSymmetricMatrix mat;
public:
///Construct using matrix @a m for the constant.
SymmetricMatrixConstantCoefficient(const DenseSymmetricMatrix &m)
: SymmetricMatrixCoefficient(m.Height()), mat(m) { }
using SymmetricMatrixCoefficient::Eval;
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseSymmetricMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) { M = mat; }
};
/** @brief A matrix coefficient with an optional scalar coefficient multiplier
\a q. The matrix function can either be represented by a std function or
a constant matrix provided when constructing this object. */
class SymmetricMatrixFunctionCoefficient : public SymmetricMatrixCoefficient
{
private:
std::function<void(const Vector &, DenseSymmetricMatrix &)> Function;
std::function<void(const Vector &, double, DenseSymmetricMatrix &)> TDFunction;
Coefficient *Q;
DenseSymmetricMatrix mat;
public:
/// Define a time-independent symmetric matrix coefficient from a std function
/** \param dim - the size of the matrix
\param F - time-independent function
\param q - optional scalar Coefficient to scale the matrix coefficient */
SymmetricMatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, DenseSymmetricMatrix &)> F,
Coefficient *q = nullptr)
: SymmetricMatrixCoefficient(dim), Function(std::move(F)), Q(q), mat(0)
{ }
/// Define a constant matrix coefficient times a scalar Coefficient
/** \param m - constant matrix
\param q - optional scalar Coefficient to scale the matrix coefficient */
SymmetricMatrixFunctionCoefficient(const DenseSymmetricMatrix &m,
Coefficient &q)
: SymmetricMatrixCoefficient(m.Height()), Q(&q), mat(m)
{ }
/// Define a time-dependent square matrix coefficient from a std function
/** \param dim - the size of the matrix
\param TDF - time-dependent function
\param q - optional scalar Coefficient to scale the matrix coefficient */
SymmetricMatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, double, DenseSymmetricMatrix &)> TDF,
Coefficient *q = nullptr)
: SymmetricMatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~SymmetricMatrixFunctionCoefficient() { }
};
/** @brief Scalar coefficient defined as the product of two scalar coefficients
or a scalar and a scalar coefficient. */
class ProductCoefficient : public Coefficient
-1
View File
@@ -1204,7 +1204,6 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
});
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
ess_tdof_list.HostRead();
if (A_i.Type() == Operator::Hypre_ParCSR)
{
HypreParMatrix * Ah;
-376
View File
@@ -48,373 +48,8 @@ void L2ZienkiewiczZhuEstimator::ComputeEstimates()
current_sequence = solution->FESpace()->GetMesh()->GetSequence();
}
KellyErrorEstimator::KellyErrorEstimator(BilinearFormIntegrator& di_,
ParGridFunction& sol_,
ParFiniteElementSpace& flux_fespace_,
const Array<int> &attributes_)
: attributes(attributes_)
, flux_integrator(&di_)
, solution(&sol_)
, flux_space(&flux_fespace_)
, own_flux_fespace(false)
{
ResetCoefficientFunctions();
}
KellyErrorEstimator::KellyErrorEstimator(BilinearFormIntegrator& di_,
ParGridFunction& sol_,
ParFiniteElementSpace* flux_fespace_,
const Array<int> &attributes_)
: attributes(attributes_)
, flux_integrator(&di_)
, solution(&sol_)
, flux_space(flux_fespace_)
, own_flux_fespace(true)
{
ResetCoefficientFunctions();
}
KellyErrorEstimator::~KellyErrorEstimator()
{
if (own_flux_fespace)
{
delete flux_space;
}
}
void KellyErrorEstimator::ResetCoefficientFunctions()
{
compute_element_coefficient = [](ParMesh* pmesh, const int e)
{
return 1.0;
};
compute_face_coefficient = [](ParMesh* pmesh, const int f,
const bool shared_face)
{
auto FT = [&]()
{
if (shared_face)
{
return pmesh->GetSharedFaceTransformations(f);
}
return pmesh->GetFaceElementTransformations(f);
}();
const auto order = FT->GetFE()->GetOrder();
// Poor man's face diameter.
double diameter = 0.0;
Vector p1(pmesh->SpaceDimension());
Vector p2(pmesh->SpaceDimension());
// NOTE: We have no direct access to vertices for shared faces,
// so we fall back to compute the positions from the element.
// This can also be modified to compute the diameter for non-linear
// geometries by sampling along geometry-specific lines.
auto vtx_intrule = Geometries.GetVertices(FT->GetGeometryType());
const auto nip = vtx_intrule->GetNPoints();
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at integration point
auto fip1 = vtx_intrule->IntPoint(i);
FT->Transform(fip1, p1);
for (int j = 0; j < nip; j++)
{
auto fip2 = vtx_intrule->IntPoint(j);
FT->Transform(fip2, p2);
diameter = std::max<double>(diameter, p2.DistanceTo(p1));
}
}
return diameter/(2.0*order);
};
}
void KellyErrorEstimator::ComputeEstimates()
{
// Remarks:
// For some context you may have to consult the documentation of
// the FaceInfo class [1]. Also, the FaceElementTransformations
// documentation [2] may be helpful to grasp what is going on. Note
// that the FaceElementTransformations also works in the non-
// conforming case to transfer the gauss points from the slave to
// the master element.
// [1]
// https://github.com/mfem/mfem/blob/02d0bfe9c18ce049c3c93a6a4208080fcfc96991/mesh/mesh.hpp#L94
// [2]
// https://github.com/mfem/mfem/blob/02d0bfe9c18ce049c3c93a6a4208080fcfc96991/fem/eltrans.hpp#L435
flux_space->Update(false);
auto xfes = solution->ParFESpace();
MFEM_ASSERT(xfes->GetVDim() == 1,
"Estimation for vector-valued problems not implemented yet.");
auto pmesh = xfes->GetParMesh();
this->error_estimates.SetSize(xfes->GetNE());
this->error_estimates = 0.0;
// 1. Compute fluxes in discontinuous space
ParGridFunction flux(flux_space);
flux = 0.0;
// We pre-sort the array to speed up the search in the following loops.
if (attributes.Size())
{
attributes.Sort();
}
Array<int> xdofs, fdofs;
Vector el_x, el_f;
for (int e = 0; e < xfes->GetNE(); e++)
{
auto attr = xfes->GetAttribute(e);
if (attributes.Size() && attributes.FindSorted(attr) == -1)
{
continue;
}
xfes->GetElementVDofs(e, xdofs);
solution->GetSubVector(xdofs, el_x);
ElementTransformation* Transf = xfes->GetElementTransformation(e);
flux_integrator->ComputeElementFlux(*xfes->GetFE(e), *Transf, el_x,
*flux_space->GetFE(e), el_f, true);
flux_space->GetElementVDofs(e, fdofs);
flux.AddElementVector(fdofs, el_f);
}
// 2. Add error contribution from local interior faces
for (int f = 0; f < pmesh->GetNumFaces(); f++)
{
auto FT = pmesh->GetFaceElementTransformations(f);
auto &int_rule = IntRules.Get(FT->FaceGeom, 2 * xfes->GetFaceOrder(f));
const auto nip = int_rule.GetNPoints();
if (pmesh->FaceIsInterior(f))
{
int Inf1, Inf2, NCFace;
pmesh->GetFaceInfos(f, &Inf1, &Inf2, &NCFace);
// Convention
// * Conforming face: Face side with smaller element id handles
// the integration
// * Non-conforming face: The slave handles the integration.
// See FaceInfo documentation for details.
bool isNCSlave = FT->Elem2No >= 0 && NCFace >= 0;
bool isConforming = FT->Elem2No >= 0 && NCFace == -1;
if ((FT->Elem1No < FT->Elem2No && isConforming) || isNCSlave)
{
if (attributes.Size() &&
(attributes.FindSorted(FT->Elem1->Attribute) == -1
|| attributes.FindSorted(FT->Elem2->Attribute) == -1))
{
continue;
}
IntegrationRule eir;
Vector jumps(nip);
// Integral over local half face on the side of e₁
// i.e. the numerical integration of ∫ flux ⋅ n dS₁
for (int i = 0; i < nip; i++)
{
// Evaluate flux at IP
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc1.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem1No, ip, val);
// And build scalar product with normal
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
FT->Loc1.Transf.SetIntPoint(&fip);
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) = val * normal * fip.weight * FT->Face->Weight();
}
// Subtract integral over half face of e₂
// i.e. the numerical integration of ∫ flux ⋅ n dS₂
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at IP
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc2.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem2No, ip, val);
// And build scalar product with normal
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
FT->Loc1.Transf.SetIntPoint(&fip);
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) -= val * normal * fip.weight * FT->Face->Weight();
}
// Finalize "local" L₂ contribution
for (int i = 0; i < nip; i++)
{
jumps(i) *= jumps(i);
}
auto h_k_face = compute_face_coefficient(pmesh, f, false);
double jump_integral = h_k_face*jumps.Sum();
// A local face is shared between two local elements, so we
// can get away with integrating the jump only once and add
// it to both elements. To minimize communication, the jump
// of shared faces is computed locally by each process.
error_estimates(FT->Elem1No) += jump_integral;
error_estimates(FT->Elem2No) += jump_integral;
}
}
}
// 3. Add error contribution from shared interior faces
// Synchronize face data.
flux.ExchangeFaceNbrData();
for (int sf = 0; sf < pmesh->GetNSharedFaces(); sf++)
{
auto FT = pmesh->GetSharedFaceTransformations(sf, true);
if (attributes.Size() &&
(attributes.FindSorted(FT->Elem1->Attribute) == -1
|| attributes.FindSorted(FT->Elem2->Attribute) == -1))
{
continue;
}
auto &int_rule = IntRules.Get(FT->FaceGeom, 2 * xfes->GetFaceOrder(0));
const auto nip = int_rule.GetNPoints();
IntegrationRule eir;
Vector jumps(nip);
// Integral over local half face on the side of e₁
// i.e. the numerical integration of ∫ flux ⋅ n dS₁
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at integration point
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc1.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem1No, ip, val);
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
FT->Loc1.Transf.SetIntPoint(&fip);
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) = val * normal * fip.weight * FT->Face->Weight();
}
// Subtract integral over non-local half face of e₂
// i.e. the numerical integration of ∫ flux ⋅ n dS₂
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at integration point
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc2.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem2No, ip, val);
// Evaluate gauss point
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) -= val * normal * fip.weight * FT->Face->Weight();
}
// Finalize "local" L₂ contribution
for (int i = 0; i < nip; i++)
{
jumps(i) *= jumps(i);
}
auto h_k_face = compute_face_coefficient(pmesh, sf, true);
double jump_integral = h_k_face*jumps.Sum();
error_estimates(FT->Elem1No) += jump_integral;
// We skip "error_estimates(FT->Elem2No) += jump_integral"
// because the error is stored on the remote process and
// recomputed there.
}
// Finalize element errors
for (int e = 0; e < xfes->GetNE(); e++)
{
auto factor = compute_element_coefficient(pmesh, e);
// The sqrt belongs to the norm and hₑ to the indicator.
error_estimates(e) = sqrt(factor * error_estimates(e));
}
current_sequence = solution->FESpace()->GetMesh()->GetSequence();
// Finish by computing the global error.
double process_local_error = error_estimates.Sum();
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
MPI_SUM, xfes->GetComm());
}
#endif // MFEM_USE_MPI
void LpErrorEstimator::ComputeEstimates()
{
MFEM_VERIFY(coef != NULL || vcoef != NULL,
@@ -429,17 +64,6 @@ void LpErrorEstimator::ComputeEstimates()
{
sol->ComputeElementLpErrors(local_norm_p, *vcoef, error_estimates);
}
#ifdef MFEM_USE_MPI
total_error = error_estimates.Sum();
auto pfes = dynamic_cast<ParFiniteElementSpace*>(sol->FESpace());
if (pfes)
{
auto process_local_error = total_error;
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
MPI_SUM, pfes->GetComm());
}
#endif // MFEM_USE_MPI
total_error = pow(total_error, 1.0/local_norm_p);
current_sequence = sol->FESpace()->GetMesh()->GetSequence();
}
+11 -189
View File
@@ -12,8 +12,6 @@
#ifndef MFEM_ERROR_ESTIMATORS
#define MFEM_ERROR_ESTIMATORS
#include <functional>
#include "../config/config.hpp"
#include "../linalg/vector.hpp"
#include "bilinearform.hpp"
@@ -41,11 +39,6 @@ public:
class ErrorEstimator : public AbstractErrorEstimator
{
public:
/// Return the total error from the last error estimate.
/** @note This method is optional for derived classes to override and the
base class implementation simply returns 0. */
virtual double GetTotalError() const { return 0.0; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors() = 0;
@@ -155,8 +148,8 @@ public:
own_flux_fes(false)
{ }
/** @brief Consider the coefficient in BilinearFormIntegrator to calculate
the fluxes for the error estimator.*/
/** @brief Consider the coefficient in BilinearFormIntegrator to calculate the
fluxes for the error estimator.*/
void SetWithCoeff(bool w_coeff = true) { with_coeff = w_coeff; }
/** @brief Enable/disable anisotropic estimates. To enable this option, the
@@ -173,10 +166,10 @@ public:
void SetFluxAveraging(int fa) { flux_averaging = fa; }
/// Return the total error from the last error estimate.
virtual double GetTotalError() const override { return total_error; }
double GetTotalError() const { return total_error; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors() override
virtual const Vector &GetLocalErrors()
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
@@ -185,14 +178,14 @@ public:
/** @brief Get an Array<int> with anisotropic flags for all mesh elements.
Return an empty array when anisotropic estimates are not available or
enabled. */
virtual const Array<int> &GetAnisotropicFlags() override
virtual const Array<int> &GetAnisotropicFlags()
{
if (MeshIsModified()) { ComputeEstimates(); }
return aniso_flags;
}
/// Reset the error estimator.
virtual void Reset() override { current_sequence = -1; }
virtual void Reset() { current_sequence = -1; }
/** @brief Destroy a ZienkiewiczZhuEstimator object. Destroys, if owned, the
FiniteElementSpace, flux_space. */
@@ -299,17 +292,17 @@ public:
void SetLocalErrorNormP(int p) { local_norm_p = p; }
/// Return the total error from the last error estimate.
virtual double GetTotalError() const override { return total_error; }
double GetTotalError() const { return total_error; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors() override
virtual const Vector &GetLocalErrors()
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
}
/// Reset the error estimator.
virtual void Reset() override { current_sequence = -1; }
virtual void Reset() { current_sequence = -1; }
/** @brief Destroy a L2ZienkiewiczZhuEstimator object. Destroys, if owned,
the FiniteElementSpace, flux_space. */
@@ -321,7 +314,6 @@ public:
#endif // MFEM_USE_MPI
/** @brief The LpErrorEstimator class compares the solution to a known
coefficient.
@@ -340,8 +332,6 @@ protected:
int local_norm_p;
Vector error_estimates;
double total_error = 0.0;
Coefficient * coef;
VectorCoefficient * vcoef;
GridFunction * sol;
@@ -393,10 +383,10 @@ public:
void SetCoef(VectorCoefficient &A) { vcoef = &A; }
/// Reset the error estimator.
virtual void Reset() override { current_sequence = -1; }
virtual void Reset() { current_sequence = -1; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors() override
virtual const Vector &GetLocalErrors()
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
@@ -406,174 +396,6 @@ public:
virtual ~LpErrorEstimator() {}
};
#ifdef MFEM_USE_MPI
/** @brief The KellyErrorEstimator class provides a fast error indication
strategy for smooth scalar parallel problems.
The Kelly error indicator is based on the following papers:
Kelly, D. W., et al. "A posteriori error analysis and adaptive processes in
the finite element method: Part IError analysis." International journal for
numerical methods in engineering 19.11 (1983): 1593-1619.
De SR Gago, J. P., et al. "A posteriori error analysis and adaptive
processes in the finite element method: Part IIAdaptive mesh refinement."
International journal for numerical methods in engineering 19.11 (1983):
1621-1656.
It can be roughly described by:
||(u-uₕ)|| ( C hₑ (hₖ |J[uₕ]|²) dS )
where "e" denotes an element, |||| the corresponding local norm and k the
corresponding faces. u is the analytic solution and uₕ the discretized
solution. hₖ and hₑ are factors dependend on the face and element geometry.
J is the jump function, i.e. the difference between the limits at each point
for each side of the face. A custom method to compute hₖ can be provided. It
is also possible to estimate the error only on a subspace by feeding this
class an attribute array describing the subspace.
@note This algorithm is only for Poisson problems a proper error esimator.
The current implementation does not reflect this, because the "C" factor is
not included.
It further assumes that the approximation error at the boundary is small
enough, as the implementation ignores boundary faces.
*/
class KellyErrorEstimator final : public ErrorEstimator
{
public:
/// Function type to compute the local coefficient hₑ of an element.
using ElementCoefficientFunction =
std::function<double(ParMesh*, const int)>;
/** @brief Function type to compute the local coefficient hₖ of a face. The
third argument is true for shared faces and false for local faces. */
using FaceCoefficientFunction =
std::function<double(ParMesh*, const int, const bool)>;
private:
int current_sequence = -1;
Vector error_estimates;
double total_error = 0.0;
Array<int> attributes;
/** @brief A method to compute hₑ on per-element basis.
This method weights the error approximation on the element level.
Defaults to hₑ=1.0.
*/
ElementCoefficientFunction compute_element_coefficient;
/** @brief A method to compute hₖ on per-face basis.
This method weights the error approximation on the face level. The
background here is that classical Kelly error estimator implementations
approximate the geometrical characteristic hₖ with the face diameter,
which should be also be a possibility in this implementation.
Defaults to hₖ=diameter/2p.
*/
FaceCoefficientFunction compute_face_coefficient;
BilinearFormIntegrator* flux_integrator; ///< Not owned.
ParGridFunction* solution; ///< Not owned.
ParFiniteElementSpace*
flux_space; /**< @brief Ownership based on own_flux_fes. */
bool own_flux_fespace; ///< Ownership flag for flux_space.
/// Check if the mesh of the solution was modified.
bool MeshIsModified()
{
long mesh_sequence = solution->FESpace()->GetMesh()->GetSequence();
MFEM_ASSERT(mesh_sequence >= current_sequence,
"improper mesh update sequence");
return (mesh_sequence > current_sequence);
}
/** @brief Compute the element error estimates.
Algorithm outline:
1. Compute flux field for each element
2. Add error contribution from local interior faces
3. Add error contribution from shared interior faces
4. Finalize by computing hₖ and scale errors.
*/
void ComputeEstimates();
public:
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinearform to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
error should be estimated. An empty array results in
estimating the error over the complete domain.
*/
KellyErrorEstimator(BilinearFormIntegrator& di_, ParGridFunction& sol_,
ParFiniteElementSpace& flux_fes_,
const Array<int> &attributes_ = Array<int>());
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinearform to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
error should be estimated. An empty array results in
estimating the error over the complete domain.
*/
KellyErrorEstimator(BilinearFormIntegrator& di_, ParGridFunction& sol_,
ParFiniteElementSpace* flux_fes_,
const Array<int> &attributes_ = Array<int>());
~KellyErrorEstimator();
/// Get a Vector with all element errors.
const Vector& GetLocalErrors() override
{
if (MeshIsModified())
{
ComputeEstimates();
}
return error_estimates;
}
/// Reset the error estimator.
void Reset() override { current_sequence = -1; };
virtual double GetTotalError() const override { return total_error; }
/** @brief Change the method to compute hₑ on a per-element basis.
@param compute_element_coefficient_
A function taking a mesh and an element index to
compute the local hₑ for the element.
*/
void SetElementCoefficientFunction(ElementCoefficientFunction
compute_element_coefficient_)
{
compute_element_coefficient = compute_element_coefficient_;
}
/** @brief Change the method to compute hₖ on a per-element basis.
@param compute_face_coefficient_
A function taking a mesh and a face index to
compute the local hₖ for the face.
*/
void SetFaceCoefficientFunction(
FaceCoefficientFunction
compute_face_coefficient_)
{
compute_face_coefficient = compute_face_coefficient_;
}
/// Change the coefficients back to default as described above.
void ResetCoefficientFunctions();
};
#endif // MFEM_USE_MPI
} // namespace mfem
#endif // MFEM_ERROR_ESTIMATORS
+12 -126
View File
@@ -435,7 +435,7 @@ void ScalarFiniteElement::ScalarLocalInterpolation(
IntegrationPoint f_ip;
const int fs = fine_fe.GetDof(), cs = this->GetDof();
I.SetSize(fs, cs);
I.SetSize(fs, cs );
Vector fine_shape(fs), coarse_shape(cs);
DenseMatrix fine_mass(fs), fine_coarse_mass(fs, cs); // initialized with 0
const int ir_order = GetOrder() + fine_fe.GetOrder();
@@ -464,44 +464,6 @@ void ScalarFiniteElement::ScalarLocalInterpolation(
}
}
void ScalarFiniteElement::ScalarLocalRestriction(
ElementTransformation &Trans, DenseMatrix &R,
const ScalarFiniteElement &coarse_fe) const
{
// General "restriction", defined by L2 projection
double v[Geometry::MaxDim];
Vector vv (v, dim);
IntegrationPoint f_ip;
const int cs = coarse_fe.GetDof(), fs = this->GetDof();
R.SetSize(cs, fs);
Vector fine_shape(fs), coarse_shape(cs);
DenseMatrix coarse_mass(cs), coarse_fine_mass(cs, fs); // initialized with 0
const int ir_order = GetOrder() + coarse_fe.GetOrder();
const IntegrationRule &ir = IntRules.Get(coarse_fe.GetGeomType(), ir_order);
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
this->CalcShape(ip, fine_shape);
Trans.Transform(ip, vv);
f_ip.Set(v, dim);
coarse_fe.CalcShape(f_ip, coarse_shape);
AddMult_a_VVt(ip.weight, coarse_shape, coarse_mass);
AddMult_a_VWt(ip.weight, coarse_shape, fine_shape, coarse_fine_mass);
}
DenseMatrixInverse coarse_mass_inv(coarse_mass);
coarse_mass_inv.Mult(coarse_fine_mass, R);
if (map_type == INTEGRAL)
{
// assuming Trans is linear; this should be ok for all refinement types
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
R *= 1.0 / Trans.Weight();
}
}
const DofToQuad &ScalarFiniteElement::GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
@@ -596,22 +558,17 @@ void NodalFiniteElement::ProjectCurl_2D(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const
{
MFEM_ASSERT(GetMapType() == FiniteElement::INTEGRAL, "");
DenseMatrix curl_shape(fe.GetDof(), 1);
curl.SetSize(dof, fe.GetDof());
for (int i = 0; i < dof; i++)
{
fe.CalcCurlShape(Nodes.IntPoint(i), curl_shape);
double w = 1.0;
if (GetMapType() == FiniteElement::VALUE)
{
Trans.SetIntPoint(&Nodes.IntPoint(i));
w /= Trans.Weight();
}
for (int j = 0; j < fe.GetDof(); j++)
{
curl(i,j) = w * curl_shape(j,0);
curl(i,j) = curl_shape(j,0);
}
}
}
@@ -727,34 +684,17 @@ void NodalFiniteElement::Project(
{
if (fe.GetRangeType() == SCALAR)
{
MFEM_ASSERT(map_type == fe.GetMapType(), "");
Vector shape(fe.GetDof());
I.SetSize(dof, fe.GetDof());
if (map_type == fe.GetMapType())
for (int k = 0; k < dof; k++)
{
for (int k = 0; k < dof; k++)
fe.CalcShape(Nodes.IntPoint(k), shape);
for (int j = 0; j < shape.Size(); j++)
{
fe.CalcShape(Nodes.IntPoint(k), shape);
for (int j = 0; j < shape.Size(); j++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
}
}
else
{
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
fe.CalcPhysShape(Trans, shape);
if (map_type == INTEGRAL)
{
shape *= Trans.Weight();
}
for (int j = 0; j < shape.Size(); j++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
}
}
@@ -1053,8 +993,6 @@ void VectorFiniteElement::Project_RT(
fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
// Transform RT face normals from reference to physical space
// vk = adj(J)^T nk
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
if (fe.GetMapType() == INTEGRAL)
{
@@ -1072,8 +1010,6 @@ void VectorFiniteElement::Project_RT(
{
s = 0.0;
}
// Project scalar basis function multiplied by each coordinate
// direction onto the transformed face normals
for (int d = 0; d < sdim; d++)
{
I(k,j+d*shape.Size()) = s*vk[d];
@@ -1083,31 +1019,7 @@ void VectorFiniteElement::Project_RT(
}
else
{
int sdim = Trans.GetSpaceDim();
double vk[Geometry::MaxDim];
DenseMatrix vshape(fe.GetDof(), sdim);
Vector vshapenk(fe.GetDof());
const bool square_J = (dim == sdim);
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
Trans.SetIntPoint(&ip);
// Transform RT face normals from reference to physical space
// vk = adj(J)^T nk
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
// Compute fe basis functions in physical space
fe.CalcVShape(Trans, vshape);
// Project fe basis functions onto transformed face normals
vshape.Mult(vk, vshapenk);
if (!square_J) { vshapenk /= Trans.Weight(); }
for (int j=0; j<vshapenk.Size(); j++)
{
I(k,j) = vshapenk(j);
}
}
mfem_error("VectorFiniteElement::Project_RT (fe version)");
}
}
@@ -1268,8 +1180,6 @@ void VectorFiniteElement::Project_ND(
fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
// Transform ND edge tengents from reference to physical space
// vk = J tk
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
if (fe.GetMapType() == INTEGRAL)
{
@@ -1287,8 +1197,6 @@ void VectorFiniteElement::Project_ND(
{
s = 0.0;
}
// Project scalar basis function multiplied by each coordinate
// direction onto the transformed edge tangents
for (int d = 0; d < sdim; d++)
{
I(k, j + d*shape.Size()) = s*vk[d];
@@ -1298,29 +1206,7 @@ void VectorFiniteElement::Project_ND(
}
else
{
int sdim = Trans.GetSpaceDim();
double vk[Geometry::MaxDim];
DenseMatrix vshape(fe.GetDof(), sdim);
Vector vshapetk(fe.GetDof());
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
Trans.SetIntPoint(&ip);
// Transform ND edge tangents from reference to physical space
// vk = J tk
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
// Compute fe basis functions in physical space
fe.CalcVShape(Trans, vshape);
// Project fe basis functions onto transformed edge tangents
vshape.Mult(vk, vshapetk);
for (int j=0; j<vshapetk.Size(); j++)
{
I(k, j) = vshapetk(j);
}
}
mfem_error("VectorFiniteElement::Project_ND (fe version)");
}
}
+1 -74
View File
@@ -504,18 +504,12 @@ public:
/** @brief Given a coefficient and a transformation, compute its projection
(approximation) in the local finite dimensional space in terms
of the degrees of freedom. */
/** The approximation used to project is usually local interpolation of
degrees of freedom. The derived class could use other methods not
implemented yet, e.g. local L2 projection. */
virtual void Project(Coefficient &coeff,
ElementTransformation &Trans, Vector &dofs) const;
/** @brief Given a vector coefficient and a transformation, compute its
projection (approximation) in the local finite dimensional space
in terms of the degrees of freedom. (VectorFiniteElements) */
/** The approximation used to project is usually local interpolation of
degrees of freedom. The derived class could use other methods not
implemented yet, e.g. local L2 projection. */
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const;
@@ -672,7 +666,7 @@ public:
const ScalarFiniteElement &fine_fe) const;
/** @brief Get matrix @a I "Interpolation" defined through local
L2-projection in the space defined by the @a fine_fe. */
L2-projection in the space defined by the @a fine_fe. */
/** If the "fine" elements cannot represent all basis functions of the
"coarse" element, then boundary values from different sub-elements are
generally different. */
@@ -680,15 +674,6 @@ public:
DenseMatrix &I,
const ScalarFiniteElement &fine_fe) const;
/** @brief Get restriction matrix @a R defined through local L2-projection
in the space defined by the @a coarse_fe. */
/** If the "fine" elements cannot represent all basis functions of the
"coarse" element, then boundary values from different sub-elements are
generally different. */
void ScalarLocalRestriction(ElementTransformation &Trans,
DenseMatrix &R,
const ScalarFiniteElement &coarse_fe) const;
virtual const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const;
};
@@ -769,10 +754,6 @@ public:
DenseMatrix &I) const
{ ScalarLocalInterpolation(Trans, I, *this); }
virtual void GetLocalRestriction(ElementTransformation &Trans,
DenseMatrix &R) const
{ ScalarLocalRestriction(Trans, R, *this); }
virtual void GetTransferMatrix(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &I) const
@@ -819,25 +800,11 @@ protected:
void CalcVShape_ND(ElementTransformation &Trans,
DenseMatrix &shape) const;
/** @brief Project a vector coefficient onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@param vc Vector coefficient to be projected
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_RT(const double *nk, const Array<int> &d2n,
VectorCoefficient &vc, ElementTransformation &Trans,
Vector &dofs) const;
/// Projects the vector of values given at FE nodes to RT space
/** Project vector values onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@param vc Vector values at each interpolation point
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_RT(const double *nk, const Array<int> &d2n,
Vector &vc, ElementTransformation &Trans,
Vector &dofs) const;
@@ -847,19 +814,6 @@ protected:
const double *nk, const Array<int> &d2n,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const;
/** @brief Project vector-valued basis functions onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@param fe Vector-valued finite element basis
@param Trans Transformation from reference to physical coordinates
@param I Expansion coefficients for the approximation of each basis
function
Note: If the FiniteElement, fe, is scalar-valued the projection will
assume that a FiniteElementSpace is being used to define a vector
field using the scalar basis functions for each component of the
vector field.
*/
void Project_RT(const double *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const;
@@ -879,25 +833,11 @@ protected:
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const;
/** @brief Project a vector coefficient onto the ND basis functions
@param tk Edge tangent vectors for this element type
@param d2t Offset into tk for each degree of freedom
@param vc Vector coefficient to be projected
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_ND(const double *tk, const Array<int> &d2t,
VectorCoefficient &vc, ElementTransformation &Trans,
Vector &dofs) const;
/// Projects the vector of values given at FE nodes to ND space
/** Project vector values onto the ND basis functions
@param tk Edge tangent vectors for this element type
@param d2t Offset into tk for each degree of freedom
@param vc Vector values at each interpolation point
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_ND(const double *tk, const Array<int> &d2t,
Vector &vc, ElementTransformation &Trans,
Vector &dofs) const;
@@ -907,19 +847,6 @@ protected:
const double *tk, const Array<int> &d2t,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const;
/** @brief Project vector-valued basis functions onto the ND basis functions
@param tk Edge tangent vectors for this element type
@param d2t Offset into tk for each degree of freedom
@param fe Vector-valued finite element basis
@param Trans Transformation from reference to physical coordinates
@param I Expansion coefficients for the approximation of each basis
function
Note: If the FiniteElement, fe, is scalar-valued the projection will
assume that a FiniteElementSpace is being used to define a vector
field using the scalar basis functions for each component of the
vector field.
*/
void Project_ND(const double *tk, const Array<int> &d2t,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const;
+1 -2
View File
@@ -2608,9 +2608,9 @@ const Operator &GridTransfer::MakeTrueOperator(
else // Parallel() == true
{
#ifdef MFEM_USE_MPI
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
if (oper_type == Operator::Hypre_ParCSR)
{
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
const ParFiniteElementSpace *pfes_in =
dynamic_cast<const ParFiniteElementSpace *>(&fes_in);
const ParFiniteElementSpace *pfes_out =
@@ -2638,7 +2638,6 @@ const Operator &GridTransfer::MakeTrueOperator(
}
else if (oper_type == Operator::ANY_TYPE)
{
const Operator *out_R = fes_out.GetRestrictionOperator();
t_oper.Reset(new TripleProductOperator(
out_R, &oper, fes_in.GetProlongationMatrix(),
false, false, false));
+1 -16
View File
@@ -330,18 +330,6 @@ public:
virtual const Operator *GetProlongationMatrix() const
{ return GetConformingProlongation(); }
/// Return an operator that performs the transpose of GetRestrictionOperator
/** The returned operator is owned by the FiniteElementSpace. In serial this
is the same as GetProlongationMatrix() */
virtual const Operator *GetRestrictionTransposeOperator() const
{ return GetConformingProlongation(); }
/// An abstract operator that performs the same action as GetRestrictionMatrix
/** In some cases this is an optimized matrix-free implementation. The
returned operator is owned by the FiniteElementSpace. */
virtual const Operator *GetRestrictionOperator() const
{ return GetConformingRestriction(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ return GetConformingRestriction(); }
@@ -583,7 +571,7 @@ public:
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. */
virtual const FiniteElement *GetFE(int i) const;
const FiniteElement *GetFE(int i) const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th boundary face in the mesh object. */
@@ -768,9 +756,6 @@ public:
/// Return the total number of quadrature points.
int GetSize() const { return size; }
/// Return the order of the quadrature rule(s) used by all elements.
int GetOrder() const { return order; }
/// Returns the mesh
inline Mesh *GetMesh() const { return mesh; }
+13 -41
View File
@@ -15,10 +15,6 @@
#include "../mesh/nurbs.hpp"
#include "../general/text.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
#include <limits>
#include <cstring>
#include <string>
@@ -26,7 +22,6 @@
#include <iostream>
#include <algorithm>
namespace mfem
{
@@ -467,26 +462,15 @@ const
fes->GetElementDofs(i, dofs);
fes->DofsToVDofs(vdim-1, dofs);
const FiniteElement *FElem = fes->GetFE(i);
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
"invalid FE map type");
int dof = FElem->GetDof();
Vector DofVal(dof), loc_data(dof);
GetSubVector(dofs, loc_data);
if (FElem->GetMapType() == FiniteElement::VALUE)
for (int k = 0; k < n; k++)
{
for (int k = 0; k < n; k++)
{
FElem->CalcShape(ir.IntPoint(k), DofVal);
vals(k) = DofVal * loc_data;
}
}
else
{
ElementTransformation *Tr = fes->GetElementTransformation(i);
for (int k = 0; k < n; k++)
{
Tr->SetIntPoint(&ir.IntPoint(k));
FElem->CalcPhysShape(*Tr, DofVal);
vals(k) = DofVal * loc_data;
}
FElem->CalcShape(ir.IntPoint(k), DofVal);
vals(k) = DofVal * loc_data;
}
}
@@ -1000,14 +984,15 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
if (FElem->GetRangeType() == FiniteElement::SCALAR)
{
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
"invalid FE map type");
Vector shape(dof);
int vdim = fes->GetVDim();
vals.SetSize(vdim, nip);
for (int j = 0; j < nip; j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T.SetIntPoint(&ip);
FElem->CalcPhysShape(T, shape);
FElem->CalcShape(ip, shape);
for (int k = 0; k < vdim; k++)
{
@@ -1565,16 +1550,18 @@ void GridFunction::GetGradient(ElementTransformation &T, Vector &grad) const
{
case ElementTransformation::ELEMENT:
{
const FiniteElement *fe = fes->GetFE(T.ElementNo);
const FiniteElement * fe = fes->GetFE(T.ElementNo);
MFEM_ASSERT(fe->GetMapType() == FiniteElement::VALUE,
"invalid FE map type");
int spaceDim = fes->GetMesh()->SpaceDimension();
int dim = fe->GetDim(), dof = fe->GetDof();
DenseMatrix dshape(dof, dim);
Vector lval, gh(dim);
Array<int> dofs;
grad.SetSize(spaceDim);
GetElementDofValues(T.ElementNo, lval);
fes->GetElementDofs(T.ElementNo, dofs);
GetSubVector(dofs, lval);
fe->CalcDShape(T.GetIntPoint(), dshape);
dshape.MultTranspose(lval, gh);
T.InverseJacobian().MultTranspose(gh, grad);
@@ -1744,13 +1731,6 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
}
}
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
{
Array<int> dof_idx;
fes->GetElementVDofs(el, dof_idx);
GetSubVector(dof_idx, dof_vals);
}
void GridFunction::ProjectGridFunction(const GridFunction &src)
{
Mesh *mesh = fes->GetMesh();
@@ -3813,15 +3793,7 @@ double ZZErrorEstimator(BilinearFormIntegrator &blfi,
}
}
}
#ifdef MFEM_USE_MPI
auto pfes = dynamic_cast<ParFiniteElementSpace*>(ufes);
if (pfes)
{
auto process_local_error = total_error;
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
MPI_SUM, pfes->GetComm());
}
#endif // MFEM_USE_MPI
return std::sqrt(total_error);
}
+5 -24
View File
@@ -325,10 +325,6 @@ public:
Both FE spaces should be scalar and on the same mesh. */
void GetElementAverages(GridFunction &avgs) const;
/** Sets the output vector @a dof_vals to the values of the degrees of
freedom of element @a el. */
virtual void GetElementDofValues(int el, Vector &dof_vals) const;
/** Impose the given bounds on the function's DOFs while preserving its local
* integral (described in terms of the given weights) on the i'th element
* through SLBPQ optimization.
@@ -349,30 +345,14 @@ public:
projection matrix. */
void ProjectGridFunction(const GridFunction &src);
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection computation depends on the choice of the FiniteElementSpace
#fes. Note that this is usually interpolation at the degrees of freedom
in each element (not L2 projection). */
virtual void ProjectCoefficient(Coefficient &coeff);
/** @brief Project @a coeff Coefficient to @a this GridFunction, using one
element for each degree of freedom in @a dofs and nodal interpolation on
that element. */
void ProjectCoefficient(Coefficient &coeff, Array<int> &dofs, int vd = 0);
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction. The
projection computation depends on the choice of the FiniteElementSpace
#fes. Note that this is usually interpolation at the degrees of freedom
in each element (not L2 projection).*/
void ProjectCoefficient(VectorCoefficient &vcoeff);
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction, using
one element for each degree of freedom in @a dofs and nodal interpolation
on that element. */
void ProjectCoefficient(VectorCoefficient &vcoeff, Array<int> &dofs);
/** @brief Analogous to the version with argument @a vcoeff VectorCoefficient
but using an array of scalar coefficients for each component. */
void ProjectCoefficient(Coefficient *coeff[]);
/** @brief Project a discontinuous vector coefficient as a grid function on
@@ -482,10 +462,11 @@ public:
/// Returns the Face Jumps error for L2 elements, with 1/h scaling.
MFEM_DEPRECATED
double ComputeDGFaceJumpError(Coefficient *exsol,
Coefficient *ell_coeff,
double Nu,
const IntegrationRule *irs[] = NULL) const;
virtual double ComputeDGFaceJumpError(Coefficient *exsol,
Coefficient *ell_coeff,
double Nu,
const IntegrationRule *irs[] = NULL)
const;
/** This method is kept for backward compatibility.
+24 -75
View File
@@ -14,40 +14,11 @@
namespace mfem
{
Multigrid::Multigrid()
: cycleType(CycleType::VCYCLE), preSmoothingSteps(1), postSmoothingSteps(1)
Multigrid::Multigrid(const FiniteElementSpaceHierarchy& fespaces_)
: fespaces(fespaces_), cycleType(CycleType::VCYCLE), preSmoothingSteps(1),
postSmoothingSteps(1)
{}
Multigrid::Multigrid(const Array<Operator*>& operators_,
const Array<Solver*>& smoothers_,
const Array<Operator*>& prolongations_,
const Array<bool>& ownedOperators_,
const Array<bool>& ownedSmoothers_,
const Array<bool>& ownedProlongations_)
: Solver(operators_.Last()->NumRows()), cycleType(CycleType::VCYCLE),
preSmoothingSteps(1), postSmoothingSteps(1),
X(operators_.Size()), Y(X.Size()), R(X.Size()), Z(X.Size())
{
operators_.Copy(operators);
smoothers_.Copy(smoothers);
prolongations_.Copy(prolongations);
ownedOperators_.Copy(ownedOperators);
ownedSmoothers_.Copy(ownedSmoothers);
ownedProlongations_.Copy(ownedProlongations);
for (int level = 0; level < operators.Size(); ++level)
{
X[level] = new Vector(operators[level]->NumRows());
*X[level] = 0.0;
Y[level] = new Vector(operators[level]->NumRows());
*Y[level] = 0.0;
R[level] = new Vector(operators[level]->NumRows());
*R[level] = 0.0;
Z[level] = new Vector(operators[level]->NumRows());
*Z[level] = 0.0;
}
}
Multigrid::~Multigrid()
{
for (int i = 0; i < operators.Size(); ++i)
@@ -66,21 +37,26 @@ Multigrid::~Multigrid()
delete Z[i];
}
for (int i = 0; i < prolongations.Size(); ++i)
{
if (ownedProlongations[i])
{
delete prolongations[i];
}
}
operators.DeleteAll();
smoothers.DeleteAll();
prolongations.DeleteAll();
X.DeleteAll();
Y.DeleteAll();
R.DeleteAll();
Z.DeleteAll();
for (int i = 0; i < bfs.Size(); ++i)
{
delete bfs[i];
}
bfs.DeleteAll();
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
{
delete essentialTrueDofs[i];
}
essentialTrueDofs.DeleteAll();
}
void Multigrid::AddLevel(Operator* opr, Solver* smoother, bool ownOperator,
@@ -192,7 +168,8 @@ void Multigrid::Cycle(int level) const
subtract(*X[level], *R[level], *R[level]);
// Restrict residual
GetProlongationAtLevel(level - 1)->MultTranspose(*R[level], *X[level - 1]);
fespaces.GetProlongationAtLevel(level - 1)->MultTranspose(*R[level],
*X[level - 1]);
// Init zeros
*Y[level - 1] = 0.0;
@@ -209,7 +186,7 @@ void Multigrid::Cycle(int level) const
}
// Prolongate
GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
fespaces.GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
// Add update
*Y[level] += *R[level];
@@ -221,44 +198,16 @@ void Multigrid::Cycle(int level) const
}
}
const Operator* Multigrid::GetProlongationAtLevel(int level) const
{
return prolongations[level];
}
GeometricMultigrid::~GeometricMultigrid()
{
for (int i = 0; i < bfs.Size(); ++i)
{
delete bfs[i];
}
bfs.DeleteAll();
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
{
delete essentialTrueDofs[i];
}
essentialTrueDofs.DeleteAll();
}
void GeometricMultigrid::FormFineLinearSystem(Vector& x, Vector& b,
OperatorHandle& A,
Vector& X, Vector& B)
void Multigrid::FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A,
Vector& X, Vector& B)
{
bfs.Last()->FormLinearSystem(*essentialTrueDofs.Last(), x, b, A, X, B);
}
void GeometricMultigrid::RecoverFineFEMSolution(const Vector& X,
const Vector& b, Vector& x)
void Multigrid::RecoverFineFEMSolution(const Vector& X, const Vector& b,
Vector& x)
{
bfs.Last()->RecoverFEMSolution(X, b, x);
}
const Operator* GeometricMultigrid::GetProlongationAtLevel(int level) const
{
return fespaces.GetProlongationAtLevel(level);
}
} // namespace mfem
+14 -45
View File
@@ -32,13 +32,16 @@ public:
};
protected:
const FiniteElementSpaceHierarchy& fespaces;
Array<Array<int>*> essentialTrueDofs;
Array<BilinearForm*> bfs;
private:
Array<Operator*> operators;
Array<Solver*> smoothers;
Array<Operator*> prolongations;
Array<bool> ownedOperators;
Array<bool> ownedSmoothers;
Array<bool> ownedProlongations;
CycleType cycleType;
int preSmoothingSteps;
@@ -50,16 +53,8 @@ protected:
mutable Array<Vector*> Z;
public:
/// Constructs an empty multigrid hierarchy.
Multigrid();
/// Constructs a multigrid hierarchy from the given inputs.
/** Inputs include operators and smoothers on all levels, prolongation
operators that go from coarser to finer levels, and ownership of the
given operators, smoothers, and prolongations. */
Multigrid(const Array<Operator*>& operators_, const Array<Solver*>& smoothers_,
const Array<Operator*>& prolongations_, const Array<bool>& ownedOperators_,
const Array<bool>& ownedSmoothers_, const Array<bool>& ownedProlongations_);
/// Constructs an empty multigrid for the given FiniteElementSpaceHierarchy
Multigrid(const FiniteElementSpaceHierarchy& fespaces_);
/// Destructor
virtual ~Multigrid();
@@ -94,7 +89,7 @@ public:
/// Returns smoother at given level
Solver* GetSmootherAtLevel(int level);
/// Set cycle type and number of pre- and post-smoothing steps used by Mult
/// Set the cycle type and number of pre- and post-smoothing steps used by Mult
void SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
int postSmoothingSteps_);
@@ -104,36 +99,7 @@ public:
/// Not supported for multigrid
virtual void SetOperator(const Operator& op) override;
private:
/// Application of a smoothing step at particular level
void SmoothingStep(int level, bool transpose) const;
/// Application of a multigrid cycle at particular level
void Cycle(int level) const;
/// Returns prolongation operator at given level
virtual const Operator* GetProlongationAtLevel(int level) const;
};
/// Geometric multigrid associated with a hierarchy of finite element spaces
class GeometricMultigrid : public Multigrid
{
protected:
const FiniteElementSpaceHierarchy& fespaces;
Array<Array<int>*> essentialTrueDofs;
Array<BilinearForm*> bfs;
public:
/** Construct an empty multigrid object for the given finite element space
hierarchy @a fespaces_ */
GeometricMultigrid(const FiniteElementSpaceHierarchy& fespaces_)
: Multigrid(), fespaces(fespaces_) { }
/// Destructor
virtual ~GeometricMultigrid();
/** Form the linear system A X = B, corresponding to the operator on the
finest level of the geometric multigrid hierarchy */
/// Form the linear system A X = B, corresponding to the operator on the finest level
void FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A, Vector& X,
Vector& B);
@@ -141,8 +107,11 @@ public:
void RecoverFineFEMSolution(const Vector& X, const Vector& b, Vector& x);
private:
/// Returns prolongation operator at given level
virtual const Operator* GetProlongationAtLevel(int level) const override;
/// Application of a smoothing step at particular level
void SmoothingStep(int level, bool transpose) const;
/// Application of a cycle at particular level
void Cycle(int level) const;
};
} // namespace mfem
-32
View File
@@ -565,38 +565,6 @@ HypreParMatrix* ParDiscreteLinearOperator::ParallelAssemble() const
return RAP;
}
void ParDiscreteLinearOperator::ParallelAssemble(OperatorHandle &A)
{
// construct the rectangular block-diagonal matrix dA
OperatorHandle dA(A.Type());
dA.MakeRectangularBlockDiag(domain_fes->GetComm(),
range_fes->GlobalVSize(),
domain_fes->GlobalVSize(),
range_fes->GetDofOffsets(),
domain_fes->GetDofOffsets(),
mat);
OperatorHandle R_test_transpose(A.Type()), P_trial(A.Type());
// TODO - construct the Dof_TrueDof_Matrix directly in the required format.
R_test_transpose.ConvertFrom(range_fes->Dof_TrueDof_Matrix());
P_trial.ConvertFrom(domain_fes->Dof_TrueDof_Matrix());
A.MakeRAP(R_test_transpose, dA, P_trial);
}
void ParDiscreteLinearOperator::FormRectangularSystemMatrix(OperatorHandle &A)
{
if (ext)
{
Array<int> empty;
ext->FormRectangularSystemOperator(empty, empty, A);
return;
}
mfem_error("not implemented!");
}
void ParDiscreteLinearOperator::GetParBlocks(Array2D<HypreParMatrix *> &blocks)
const
{
-11
View File
@@ -160,9 +160,6 @@ public:
/// Get the parallel finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return pfes->GetProlongationMatrix(); }
/// Get the transpose of GetRestriction, useful for matrix-free RAP
virtual const Operator *GetRestrictionTranspose() const
{ return pfes->GetRestrictionTransposeOperator(); }
/// Get the parallel finite element space restriction matrix
virtual const Operator *GetRestriction() const
{ return pfes->GetRestrictionMatrix(); }
@@ -304,18 +301,10 @@ public:
/// Returns the matrix "assembled" on the true dofs
HypreParMatrix *ParallelAssemble() const;
/** @brief Returns the matrix assembled on the true dofs, i.e.
@a A = R_test A_local P_trial, in the format (type id) specified by
@a A. */
void ParallelAssemble(OperatorHandle &A);
/** Extract the parallel blocks corresponding to the vector dimensions of the
domain and range parallel finite element spaces */
void GetParBlocks(Array2D<HypreParMatrix *> &blocks) const;
/** @brief Return in @a A a parallel (on truedofs) version of this operator. */
virtual void FormRectangularSystemMatrix(OperatorHandle &A);
virtual ~ParDiscreteLinearOperator() { }
};
+49 -132
View File
@@ -101,8 +101,6 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
P = NULL;
Pconf = NULL;
Rconf = NULL;
R_transpose = NULL;
R = NULL;
num_face_nbr_dofs = -1;
@@ -501,12 +499,6 @@ void ParFiniteElementSpace::GetFaceDofs(int i, Array<int> &dofs) const
}
}
const FiniteElement *ParFiniteElementSpace::GetFE(int i) const
{
int ne = mesh->GetNE();
if (i >= ne) { return GetFaceNbrFE(i - ne); }
else { return FiniteElementSpace::GetFE(i); }
}
const Operator *ParFiniteElementSpace::GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
@@ -929,45 +921,6 @@ const Operator *ParFiniteElementSpace::GetProlongationMatrix() const
}
}
const Operator *ParFiniteElementSpace::GetRestrictionOperator() const
{
if (Conforming())
{
if (Rconf) { return Rconf; }
if (NRanks == 1)
{
R_transpose = new IdentityOperator(GetTrueVSize());
}
else
{
if (!Device::Allows(Backend::DEVICE_MASK))
{
R_transpose = new ConformingProlongationOperator(*this, true);
}
else
{
R_transpose =
new DeviceConformingProlongationOperator(*this, true);
}
}
Rconf = new TransposeOperator(R_transpose);
return Rconf;
}
else
{
Dof_TrueDof_Matrix();
R_transpose = new TransposeOperator(R);
return R;
}
}
const Operator *ParFiniteElementSpace::GetRestrictionTransposeOperator() const
{
GetRestrictionOperator();
return R_transpose;
}
void ParFiniteElementSpace::ExchangeFaceNbrData()
{
if (num_face_nbr_dofs >= 0) { return; }
@@ -2881,8 +2834,6 @@ void ParFiniteElementSpace::Destroy()
delete P; P = NULL;
delete Pconf; Pconf = NULL;
delete Rconf; Rconf = NULL;
delete R_transpose; R_transpose = NULL;
delete R; R = NULL;
delete gcomm; gcomm = NULL;
@@ -3008,12 +2959,12 @@ void ParFiniteElementSpace::Update(bool want_transform)
}
}
ConformingProlongationOperator::ConformingProlongationOperator(
const ParFiniteElementSpace &pfes, bool local_)
const ParFiniteElementSpace &pfes)
: Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
external_ldofs(),
gc(pfes.GroupComm()),
local(local_)
gc(pfes.GroupComm())
{
MFEM_VERIFY(pfes.Conforming(), "");
const Table &group_ldof = gc.GroupLDofTable();
@@ -3062,14 +3013,7 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
const int m = external_ldofs.Size();
const int in_layout = 2; // 2 - input is ltdofs array
if (local)
{
y = 0.0;
}
else
{
gc.BcastBegin(const_cast<double*>(xdata), in_layout);
}
gc.BcastBegin(const_cast<double*>(xdata), in_layout);
int j = 0;
for (int i = 0; i < m; i++)
@@ -3081,10 +3025,7 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
std::copy(xdata+j-m, xdata+Width(), ydata+j);
const int out_layout = 0; // 0 - output is ldofs array
if (!local)
{
gc.BcastEnd(ydata, out_layout);
}
gc.BcastEnd(ydata, out_layout);
}
void ConformingProlongationOperator::MultTranspose(
@@ -3097,10 +3038,7 @@ void ConformingProlongationOperator::MultTranspose(
double *ydata = y.HostWrite();
const int m = external_ldofs.Size();
if (!local)
{
gc.ReduceBegin(xdata);
}
gc.ReduceBegin(xdata);
int j = 0;
for (int i = 0; i < m; i++)
@@ -3112,18 +3050,13 @@ void ConformingProlongationOperator::MultTranspose(
std::copy(xdata+j, xdata+Height(), ydata+j-m);
const int out_layout = 2; // 2 - output is an array on all ltdofs
if (!local)
{
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
}
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
}
DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
const ParFiniteElementSpace &pfes,
bool local_) :
const ParFiniteElementSpace &pfes) :
ConformingProlongationOperator(pfes),
mpi_gpu_aware(Device::GetGPUAwareMPI()),
local(local_)
mpi_gpu_aware(Device::GetGPUAwareMPI())
{
MFEM_ASSERT(pfes.Conforming(), "internal error");
const SparseMatrix *R = pfes.GetRestrictionMatrix();
@@ -3240,42 +3173,32 @@ void DeviceConformingProlongationOperator::Mult(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
BcastBeginCopy(x); // copy to 'shr_buf'
int req_counter = 0;
if (local)
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
y = 0.0;
}
else
{
BcastBeginCopy(x); // copy to 'shr_buf'
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
}
BcastLocalCopy(x, y);
if (!local)
{
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
}
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
}
DeviceConformingProlongationOperator::~DeviceConformingProlongationOperator()
@@ -3338,38 +3261,32 @@ void DeviceConformingProlongationOperator::MultTranspose(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
ReduceBeginCopy(x); // copy to 'ext_buf'
int req_counter = 0;
if (!local)
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
ReduceBeginCopy(x); // copy to 'ext_buf'
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
const int send_offset = ext_buf_offsets[nbr];
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
const int send_offset = ext_buf_offsets[nbr];
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
}
ReduceLocalCopy(x, y);
if (!local)
{
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y); // assemble from 'shr_buf'
}
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y); // assemble from 'shr_buf'
}
} // namespace mfem
+2 -29
View File
@@ -75,12 +75,6 @@ private:
/// The (block-diagonal) matrix R (restriction of dof to true dof). Owned.
mutable SparseMatrix *R;
/// Optimized action-only restriction operator for conforming meshes. Owned.
mutable Operator *Rconf;
/** Transpose of R or Rconf. For conforming mesh, this is a matrix-free
(Device)ConformingProlongationOperator, for a non-conforming mesh
this is a TransposeOperator wrapping R. */
mutable Operator *R_transpose;
ParNURBSExtension *pNURBSext() const
{ return dynamic_cast<ParNURBSExtension *>(NURBSext); }
@@ -270,12 +264,6 @@ public:
including the dofs for the edges and the vertices of the face. */
virtual void GetFaceDofs(int i, Array<int> &dofs) const;
/** Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. If @a i is greater than
or equal to the number of local mesh elements, @a i will be interpreted
as a shifted index of a face neigbor element. */
virtual const FiniteElement *GetFE(int i) const;
/** Returns an Operator that converts L-vectors to E-vectors on each face.
The parallel version is different from the serial one because of the
presence of shared faces. Shared faces are treated as interior faces,
@@ -347,16 +335,6 @@ public:
HYPRE_Int GetMyTDofOffset() const;
virtual const Operator *GetProlongationMatrix() const;
/** @brief Return logical transpose of restriction matrix, but in
non-assembled optimized matrix-free form.
The implementation is like GetProlongationMatrix, but it sets local
DOFs to the true DOF values if owned locally, otherwise zero. */
virtual const Operator *GetRestrictionTransposeOperator() const;
/** Get an Operator that performs the action of GetRestrictionMatrix(),
but potentially with a non-assembled optimized matrix-free
implementation. */
virtual const Operator *GetRestrictionOperator() const;
/// Get the R matrix which restricts a local dof vector to true dof vector.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ Dof_TrueDof_Matrix(); return R; }
@@ -411,11 +389,9 @@ class ConformingProlongationOperator : public Operator
protected:
Array<int> external_ldofs;
const GroupCommunicator &gc;
bool local;
public:
ConformingProlongationOperator(const ParFiniteElementSpace &pfes,
bool local_=false);
ConformingProlongationOperator(const ParFiniteElementSpace &pfes);
virtual void Mult(const Vector &x, Vector &y) const;
@@ -434,8 +410,6 @@ protected:
Array<int> ltdof_ldof, unq_ltdof;
Array<int> unq_shr_i, unq_shr_j;
MPI_Request *requests;
bool local;
// Kernel: copy ltdofs from 'src' to 'shr_buf' - prepare for send.
// shr_buf[i] = src[shr_ltdof[i]]
void BcastBeginCopy(const Vector &src) const;
@@ -461,8 +435,7 @@ protected:
void ReduceEndAssemble(Vector &dst) const;
public:
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes,
bool local_=false);
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes);
virtual ~DeviceConformingProlongationOperator();
-19
View File
@@ -471,25 +471,6 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
}
}
void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
{
int ne = fes->GetNE();
if (el >= ne)
{
MFEM_ASSERT(face_nbr_data.Size() > 0,
"ParGridFunction::GetElementDofValues: ExchangeFaceNbrData "
"must be called before accessing face neighbor elements.");
// Face neighbor element
Array<int> dof_idx;
pfes->GetFaceNbrElementVDofs(el - ne, dof_idx);
face_nbr_data.GetSubVector(dof_idx, dof_vals);
}
else
{
GridFunction::GetElementDofValues(el, dof_vals);
}
}
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
{
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
-6
View File
@@ -221,12 +221,6 @@ public:
const IntegrationPoint &ip,
Vector &val, Vector *tr = NULL) const;
/** Sets the output vector @a dof_vals to the values of the degrees of
freedom of element @a el. If @a el is greater than or equal to the number
of local elements, it will be interpreted as a shifted index of a face
neighbor element. */
virtual void GetElementDofValues(int el, Vector &dof_vals) const;
using GridFunction::ProjectCoefficient;
virtual void ProjectCoefficient(Coefficient &coeff);
-38
View File
@@ -420,35 +420,6 @@ void QuadratureInterpolator::MultTranspose(
MFEM_ABORT("this method is not implemented yet");
}
static void D2QValues1D(const int NE,
const Array<double> &b_,
const Vector &x_,
Vector &y_,
const int vdim = 1,
const int d1d = 0,
const int q1d = 0)
{
auto b = Reshape(b_.Read(), q1d, d1d);
auto x = Reshape(x_.Read(), d1d, vdim, NE);
auto y = Reshape(y_.Write(), vdim, q1d, NE);
MFEM_FORALL(e, NE,
{
for (int c = 0; c < vdim; c++)
{
for (int q = 0; q < q1d; ++q)
{
double val = 0.0;
for (int d = 0; d < d1d; ++d)
{
val += b(q, d) * x(d, c, e);
}
y(c, q, e) = val;
}
}
});
}
template<int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void D2QValues2D(const int NE,
@@ -660,15 +631,6 @@ static void D2QValues(const FiniteElementSpace &fes,
const int Q1D = maps->nqpt;
const int id = (vdim<<8) | (D1D<<4) | Q1D;
if (dim == 1)
{
MFEM_VERIFY(D1D <= MAX_D1D, "Orders higher than " << MAX_D1D-1
<< " are not supported!");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Quadrature rules with more than "
<< MAX_Q1D << " 1D points are not supported!");
D2QValues1D(NE, maps->B, e_vec, q_val, vdim, D1D, Q1D);
return;
}
if (dim == 2)
{
switch (id)
-25
View File
@@ -195,31 +195,6 @@ void ElementRestriction::MultTransposeUnsigned(const Vector& x, Vector& y) const
});
}
void ElementRestriction::MultLeftInverse(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
const bool t = byvdim;
auto d_offsets = offsets.Read();
auto d_indices = indices.Read();
auto d_x = Reshape(x.Read(), nd, vd, ne);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
const int nextOffset = d_offsets[i + 1];
for (int c = 0; c < vd; ++c)
{
double dofValue = 0;
const int j = nextOffset - 1;
const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
dofValue = (d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
-d_x(idx_j % nd, c, idx_j / nd);
d_y(t?c:i,t?i:c) = dofValue;
}
});
}
void ElementRestriction::BooleanMask(Vector& y) const
{
// Assumes all elements have the same number of dofs
-4
View File
@@ -57,10 +57,6 @@ public:
/// Compute MultTranspose without applying signs based on DOF orientations.
void MultTransposeUnsigned(const Vector &x, Vector &y) const;
/// Compute MultTranspose by setting (rather than adding) element
/// contributions; this is a left inverse of the Mult() operation
void MultLeftInverse(const Vector &x, Vector &y) const;
/// @brief Fills the E-vector y with `boolean` values 0.0 and 1.0 such that each
/// each entry of the L-vector is uniquely represented in `y`.
/** This means, the sum of the E-vector `y` is equal to the sum of the
+22 -229
View File
@@ -19,42 +19,6 @@ namespace mfem
// Target-matrix optimization paradigm (TMOP) mesh quality metrics.
double TMOP_Combo_QualityMetric::EvalW(const DenseMatrix &Jpt) const
{
double metric = 0.;
for (int i = 0; i < tmop_q_arr.Size(); i++)
{
metric += wt_arr[i]*tmop_q_arr[i]->EvalW(Jpt);
}
return metric;
}
void TMOP_Combo_QualityMetric::EvalP(const DenseMatrix &Jpt,
DenseMatrix &P) const
{
DenseMatrix Pt(P.Size());
for (int i = 0; i < tmop_q_arr.Size(); i++)
{
tmop_q_arr[i]->EvalP(Jpt, Pt);
Pt *= wt_arr[i];
P += Pt;
}
}
void TMOP_Combo_QualityMetric::AssembleH(const DenseMatrix &Jpt,
const DenseMatrix &DS,
const double weight,
DenseMatrix &A) const
{
DenseMatrix At(A.Size());
for (int i = 0; i < tmop_q_arr.Size(); i++)
{
tmop_q_arr[i]->AssembleH(Jpt, DS, weight, At);
At *= wt_arr[i];
A += At;
}
}
double TMOP_Metric_001::EvalW(const DenseMatrix &Jpt) const
{
ie.SetJacobian(Jpt.GetData());
@@ -196,6 +160,23 @@ double TMOP_Metric_aspratio3D::EvalW(const DenseMatrix &Jpt) const
) / 3.0;
}
// mu_14 = |T-I|^2
double TMOP_Metric_SSA2D::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
DenseMatrix Id(2,2);
Id(0,0) = 1; Id(0,1) = 0;
Id(1,0) = 0; Id(1,1) = 1;
DenseMatrix Mat(2,2);
Mat = Jpt;
Mat.Add(-1,Id);
return Mat.FNorm2();
}
double TMOP_Metric_002::EvalW(const DenseMatrix &Jpt) const
{
ie.SetJacobian(Jpt.GetData());
@@ -291,41 +272,13 @@ void TMOP_Metric_009::AssembleH(const DenseMatrix &Jpt,
ie.Assemble_ddI1b(weight, A.GetData());
}
// mu_14 = |T-I|^2
double TMOP_Metric_014::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
DenseMatrix Id(2,2);
Id(0,0) = 1; Id(0,1) = 0;
Id(1,0) = 0; Id(1,1) = 1;
DenseMatrix Mat(2,2);
Mat = Jpt;
Mat.Add(-1,Id);
return Mat.FNorm2();
}
double TMOP_Metric_022::EvalW(const DenseMatrix &Jpt) const
{
// mu_22 = (0.5*|J|^2 - det(J)) / (det(J) - tau0)
// = (0.5*I1 - I2b) / (I2b - tau0)
ie.SetJacobian(Jpt.GetData());
const double I2b = ie.Get_I2b();
double d = I2b - min_detT;
if (d < 0.0 && min_detT == 0.0)
{
// The mesh has been untangled, but it's still possible to get negative
// detJ in FD calculations, as they move the nodes around with some small
// increments and can produce negative determinants. Thus we put a small
// value in the denominator. Note that here I2b < 0.
d = - I2b * 0.1;
}
return (0.5*ie.Get_I1() - I2b) / d;
return (0.5*ie.Get_I1() - I2b) / (I2b - tau0);
}
void TMOP_Metric_022::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
@@ -334,8 +287,8 @@ void TMOP_Metric_022::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
// P = 1/(I2b - tau0)*(0.5*dI1 - dI2b) - (0.5*I1 - I2b)/(I2b - tau0)^2*dI2b
// = 0.5/(I2b - tau0)*dI1 + (tau0 - 0.5*I1)/(I2b - tau0)^2*dI2b
ie.SetJacobian(Jpt.GetData());
const double c1 = 1.0/(ie.Get_I2b() - min_detT);
Add(c1/2, ie.Get_dI1(), (min_detT - ie.Get_I1()/2)*c1*c1, ie.Get_dI2b(), P);
const double c1 = 1.0/(ie.Get_I2b() - tau0);
Add(c1/2, ie.Get_dI1(), (tau0 - ie.Get_I1()/2)*c1*c1, ie.Get_dI2b(), P);
}
void TMOP_Metric_022::AssembleH(const DenseMatrix &Jpt,
@@ -355,10 +308,10 @@ void TMOP_Metric_022::AssembleH(const DenseMatrix &Jpt,
// +0.5/(I2b - tau0)*ddI1 + z*ddI2b
ie.SetJacobian(Jpt.GetData());
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
const double c1 = 1.0/(ie.Get_I2b() - min_detT);
const double c1 = 1.0/(ie.Get_I2b() - tau0);
const double c2 = weight*c1/2;
const double c3 = c1*c2;
const double c4 = (2*min_detT - ie.Get_I1())*c3; // weight*z
const double c4 = (2*tau0 - ie.Get_I1())*c3; // weight*z
ie.Assemble_TProd(-c3, ie.Get_dI1(), ie.Get_dI2b(), A.GetData());
ie.Assemble_TProd(-2*c1*c4, ie.Get_dI2b(), A.GetData());
ie.Assemble_ddI1(c2, A.GetData());
@@ -531,23 +484,6 @@ double TMOP_Metric_085::EvalW(const DenseMatrix &Jpt) const
return Mat.FNorm2();
}
// mu_98 = 1/(tau)|T-I|^2
double TMOP_Metric_098::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
DenseMatrix Id(2,2);
Id(0,0) = 1; Id(0,1) = 0;
Id(1,0) = 0; Id(1,1) = 1;
DenseMatrix Mat(2,2);
Mat = Jpt;
Mat.Add(-1,Id);
return Mat.FNorm2()/Jtr->Det();
}
double TMOP_Metric_211::EvalW(const DenseMatrix &Jpt) const
{
// mu_211 = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^{1/2}
@@ -714,71 +650,6 @@ void TMOP_Metric_303::AssembleH(const DenseMatrix &Jpt,
ie.Assemble_ddI1b(weight/3., A.GetData());
}
double TMOP_Metric_311::EvalW(const DenseMatrix &Jpt) const
{
// mu_311 = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^{1/2}
// = (I3b - 1)^2 - I3b + sqrt(I3b^2 + eps)
ie.SetJacobian(Jpt.GetData());
const double I3b = ie.Get_I3b();
return (I3b - 1.0)*(I3b - 1.0) - I3b + std::sqrt(I3b*I3b + eps);
}
void TMOP_Metric_311::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{
ie.SetJacobian(Jpt.GetData());
const double I3b = ie.Get_I3b();
const double c = 2*I3b-3+(I3b)/(std::pow((I3b*I3b+eps),0.5));
P.Set(c, ie.Get_dI3b());
}
void TMOP_Metric_311::AssembleH(const DenseMatrix &Jpt,
const DenseMatrix &DS,
const double weight,
DenseMatrix &A) const
{
ie.SetJacobian(Jpt.GetData());
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
const double I3b = ie.Get_I3b();
const double c0 = I3b*I3b+eps;
const double c1 = 2 + 1/(pow(c0,0.5)) - I3b*I3b/(pow(c0,1.5));
const double c2 = 2*I3b - 3 + I3b/(pow(c0,0.5));
ie.Assemble_TProd(weight*c1, ie.Get_dI3b(), A.GetData());
ie.Assemble_ddI3b(c2*weight, A.GetData());
}
double TMOP_Metric_313::EvalW(const DenseMatrix &Jpt) const
{
ie.SetJacobian(Jpt.GetData());
const double I3b = ie.Get_I3b();
double d = I3b - min_detT;
if (d < 0.0 && min_detT == 0.0)
{
// The mesh has been untangled, but it's still possible to get negative
// detJ in FD calculations, as they move the nodes around with some small
// increments and can produce negative determinants. Thus we put a small
// value in the denominator. Note that here I3b < 0.
d = - I3b * 0.1;
}
const double c = std::pow(d, -2.0/3.0);
return ie.Get_I1() * c / 3.0;
}
void TMOP_Metric_313::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{
MFEM_ABORT("Metric not implemented yet.");
}
void TMOP_Metric_313::AssembleH(const DenseMatrix &Jpt,
const DenseMatrix &DS,
const double weight,
DenseMatrix &A) const
{
MFEM_ABORT("Metric not implemented yet.");
}
double TMOP_Metric_315::EvalW(const DenseMatrix &Jpt) const
{
// mu_315 = mu_15_3D = (det(J) - 1)^2
@@ -929,84 +800,6 @@ void TMOP_Metric_352::AssembleH(const DenseMatrix &Jpt,
ie.Assemble_ddI3b(weight*(c - 0.5*c*c), A.GetData());
}
double TMOP_AMetric_011::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
int dim = Jpt.Size();
DenseMatrix Jpr(dim, dim);
Mult(Jpt, *Jtr, Jpr);
double alpha = Jpr.Det(),
omega = Jtr->Det();
DenseMatrix AdjAt(dim), WtW(dim), WRK(dim), Jtrt(dim);
CalcAdjugateTranspose(Jpr, AdjAt);
Jtrt.Transpose(*Jtr);
MultAAt(Jtrt, WtW);
WtW *= 1./omega;
Mult(AdjAt, WtW, WRK);
WRK -= Jpr;
WRK *= -1.;
return (0.25/alpha)*WRK.FNorm2();
}
double TMOP_AMetric_014a::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
int dim = Jpt.Size();
DenseMatrix Jpr(dim, dim);
Mult(Jpt, *Jtr, Jpr);
double sqalpha = pow(Jpr.Det(), 0.5),
sqomega = pow(Jtr->Det(), 0.5);
return 0.5*pow(sqalpha/sqomega - sqomega/sqalpha, 2.);
}
double TMOP_AMetric_036::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
int dim = Jpt.Size();
DenseMatrix Jpr(dim, dim);
Mult(Jpt, *Jtr, Jpr); // T*W = A
double alpha = Jpr.Det(); // det(A)
Jpr -= *Jtr; // A-W
return (1./alpha)*(Jpr.FNorm2()); //(1/alpha)*(|A-W|^2)
}
double TMOP_AMetric_107a::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
int dim = Jpt.Size();
DenseMatrix Jpr(dim, dim);
Mult(Jpt, *Jtr, Jpr);
double alpha = Jpr.Det(),
aw = Jpr.FNorm()/Jtr->FNorm();
DenseMatrix W = *Jtr;
W *= aw;
Jpr -= W;
return (0.5/alpha)*Jpr.FNorm2();
}
void TargetConstructor::ComputeAvgVolume() const
{
+41 -237
View File
@@ -40,7 +40,7 @@ public:
The specified Jacobian matrix, #Jtr, can be used by metrics that cannot
be written just as a function of the target->physical Jacobian matrix,
Jpt. */
virtual void SetTargetJacobian(const DenseMatrix &_Jtr) { Jtr = &_Jtr; }
void SetTargetJacobian(const DenseMatrix &_Jtr) { Jtr = &_Jtr; }
/** @brief Evaluate the strain energy density function, W = W(Jpt).
@param[in] Jpt Represents the target->physical transformation
@@ -70,37 +70,8 @@ public:
const double weight, DenseMatrix &A) const = 0;
};
/// Abstract class used to define combination of metrics with constant coefficients.
class TMOP_Combo_QualityMetric : public TMOP_QualityMetric
{
protected:
Array<TMOP_QualityMetric *> tmop_q_arr; //not owned
Array<double> wt_arr;
public:
virtual void AddQualityMetric(TMOP_QualityMetric *tq, double wt = 1.0)
{
tmop_q_arr.Append(tq);
wt_arr.Append(wt);
}
virtual void SetTargetJacobian(const DenseMatrix &_Jtr)
{
for (int i = 0; i < tmop_q_arr.Size(); i++)
{
tmop_q_arr[i]->SetTargetJacobian(_Jtr);
}
}
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const;
};
/// 2D non-barrier metric without a type.
/// Metric without a type, 2D
class TMOP_Metric_001 : public TMOP_QualityMetric
{
protected:
@@ -116,7 +87,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 2D non-barrier Skew metric.
/// Skew metric, 2D.
class TMOP_Metric_skew2D : public TMOP_QualityMetric
{
public:
@@ -131,7 +102,7 @@ public:
{ MFEM_ABORT("Not implemented"); }
};
/// 3D non-barrier Skew metric.
/// Skew metric, 3D.
class TMOP_Metric_skew3D : public TMOP_QualityMetric
{
public:
@@ -146,7 +117,7 @@ public:
{ MFEM_ABORT("Not implemented"); }
};
/// 2D non-barrier Aspect ratio metric.
/// Aspect ratio metric, 2D.
class TMOP_Metric_aspratio2D : public TMOP_QualityMetric
{
public:
@@ -161,7 +132,7 @@ public:
{ MFEM_ABORT("Not implemented"); }
};
/// 3D non-barrier Aspect ratio metric.
/// Aspect ratio metric, 3D.
class TMOP_Metric_aspratio3D : public TMOP_QualityMetric
{
public:
@@ -176,7 +147,22 @@ public:
{ MFEM_ABORT("Not implemented"); }
};
/// 2D barrier shape (S) metric (polyconvex).
/// Shape+Size+Orientation metric, 2D.
class TMOP_Metric_SSA2D : public TMOP_QualityMetric
{
public:
// W = 0.5 (1 - cos(theta_Jpr - theta_Jtr)).
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// Shape, ideal barrier metric, 2D
class TMOP_Metric_002 : public TMOP_QualityMetric
{
protected:
@@ -192,7 +178,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 2D barrier Shape+Size (VS) metric (not polyconvex).
/// Shape & area, ideal barrier metric, 2D
class TMOP_Metric_007 : public TMOP_QualityMetric
{
protected:
@@ -208,7 +194,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 2D barrier Shape+Size (VS) metric (not polyconvex).
/// Shape & area metric, 2D
class TMOP_Metric_009 : public TMOP_QualityMetric
{
protected:
@@ -224,30 +210,15 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 2D non-barrier Shape+Size+Orientation (VOS) metric (polyconvex).
class TMOP_Metric_014 : public TMOP_QualityMetric
{
public:
// W = |T-I|^2.
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// 2D Shifted barrier form of shape metric (mu_2).
/// Shifted barrier form of metric 2 (shape, ideal barrier metric), 2D
class TMOP_Metric_022 : public TMOP_QualityMetric
{
protected:
double &min_detT;
double &tau0;
mutable InvariantsEvaluator2D<double> ie;
public:
TMOP_Metric_022(double &t0): min_detT(t0) {}
TMOP_Metric_022(double &t0): tau0(t0) {}
// W = 0.5(|J|^2 - 2det(J)) / (det(J) - tau0).
virtual double EvalW(const DenseMatrix &Jpt) const;
@@ -258,7 +229,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 2D barrier (not a shape) metric (polyconvex).
/// Shape, ideal barrier metric, 2D
class TMOP_Metric_050 : public TMOP_QualityMetric
{
protected:
@@ -274,7 +245,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 2D non-barrier size (V) metric (not polyconvex).
/// Area metric, 2D
class TMOP_Metric_055 : public TMOP_QualityMetric
{
protected:
@@ -291,7 +262,7 @@ public:
};
/// 2D barrier size (V) metric (polyconvex).
/// Area, ideal barrier metric, 2D
class TMOP_Metric_056 : public TMOP_QualityMetric
{
protected:
@@ -310,7 +281,7 @@ public:
};
/// 2D barrier shape (S) metric (not polyconvex).
/// Shape, ideal barrier metric, 2D
class TMOP_Metric_058 : public TMOP_QualityMetric
{
protected:
@@ -328,7 +299,7 @@ public:
};
/// 2D barrier size (V) metric (polyconvex).
/// Area, ideal barrier metric, 2D
class TMOP_Metric_077 : public TMOP_QualityMetric
{
protected:
@@ -345,28 +316,7 @@ public:
};
/// 2D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_Metric_080 : public TMOP_Combo_QualityMetric
{
protected:
mutable InvariantsEvaluator2D<double> ie;
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
public:
TMOP_Metric_080(double gamma_) : gamma(gamma_),
sh_metric(new TMOP_Metric_002),
sz_metric(new TMOP_Metric_077)
{
// (1-gamma) mu_2 + gamma mu_77
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual ~TMOP_Metric_080() { delete sh_metric; delete sz_metric; }
};
/// 2D barrier Shape+Orientation (OS) metric (polyconvex).
/// Shape & orientation metric, 2D.
class TMOP_Metric_085 : public TMOP_QualityMetric
{
public:
@@ -381,22 +331,7 @@ public:
{ MFEM_ABORT("Not implemented"); }
};
/// 2D barrier Shape+Size+Orientation (VOS) metric (polyconvex).
class TMOP_Metric_098 : public TMOP_QualityMetric
{
public:
// W = 1/tau |T-I|^2.
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// 2D untangling metric.
/// Untangling metric, 2D
class TMOP_Metric_211 : public TMOP_QualityMetric
{
protected:
@@ -435,7 +370,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 3D barrier Shape (S) metric.
/// Shape, ideal barrier metric, 3D
class TMOP_Metric_301 : public TMOP_QualityMetric
{
protected:
@@ -451,7 +386,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 3D barrier Shape (S) metric.
/// Shape, ideal barrier metric, 3D
class TMOP_Metric_302 : public TMOP_QualityMetric
{
protected:
@@ -467,14 +402,14 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 3D barrier Shape (S) metric.
/// Shape, ideal barrier metric, 3D
class TMOP_Metric_303 : public TMOP_QualityMetric
{
protected:
mutable InvariantsEvaluator3D<double> ie;
public:
// W = |J|^2 / 3 * det(J)^(-2/3) - 1.
// W = |J|^2 / 3 * det(J)^(2/3) - 1.
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
@@ -483,45 +418,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 3D Size (V) untangling metric.
class TMOP_Metric_311 : public TMOP_QualityMetric
{
protected:
const double eps;
mutable InvariantsEvaluator3D<double> ie;
public:
TMOP_Metric_311(double epsilon = 1e-4) : eps(epsilon) { }
// W = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^(1/2).
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const;
};
/// 3D Shape (S) metric, untangling version of 303.
class TMOP_Metric_313 : public TMOP_QualityMetric
{
protected:
double &min_detT;
mutable InvariantsEvaluator3D<double> ie;
public:
TMOP_Metric_313(double &mindet) : min_detT(mindet) { }
// W = 1/3 |J|^2 / [det(J)-tau0]^(-2/3).
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const;
};
/// 3D non-barrier Size (V) metric.
/// Volume metric, 3D
class TMOP_Metric_315 : public TMOP_QualityMetric
{
protected:
@@ -537,7 +434,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 3D barrier Size (V) metric.
/// Volume, ideal barrier metric, 3D
class TMOP_Metric_316 : public TMOP_QualityMetric
{
protected:
@@ -555,7 +452,7 @@ public:
const double weight, DenseMatrix &A) const;
};
/// 3D barrier Shape+Size (VS) metric.
/// Shape & volume, ideal barrier metric, 3D
class TMOP_Metric_321 : public TMOP_QualityMetric
{
protected:
@@ -590,99 +487,6 @@ public:
const double weight, DenseMatrix &A) const;
};
/// A-metrics
/// 2D barrier Shape (S) metric (polyconvex).
class TMOP_AMetric_011 : public TMOP_QualityMetric
{
protected:
mutable InvariantsEvaluator3D<double> ie;
public:
// (1/4 alpha) | A - (adj A)^t W^t W / omega |^2
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// 2D barrier Size (V) metric (polyconvex).
class TMOP_AMetric_014a : public TMOP_QualityMetric
{
protected:
mutable InvariantsEvaluator3D<double> ie;
public:
// 0.5 * ( sqrt(alpha/omega) - sqrt(omega/alpha) )^2
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// 2D barrier Shape+Size+Orientation (VOS) metric (polyconvex).
class TMOP_AMetric_036 : public TMOP_QualityMetric
{
protected:
mutable InvariantsEvaluator3D<double> ie;
public:
// (1/alpha) | A - W |^2
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// 2D barrier Shape+Orientation (OS) metric (polyconvex).
class TMOP_AMetric_107a : public TMOP_QualityMetric
{
protected:
mutable InvariantsEvaluator3D<double> ie;
public:
// (1/2 alpha) | A - (|A|/|W|) W |^2
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
};
/// 2D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_AMetric_126 : public TMOP_Combo_QualityMetric
{
protected:
mutable InvariantsEvaluator2D<double> ie;
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
public:
TMOP_AMetric_126(double gamma_) : gamma(gamma_),
sh_metric(new TMOP_AMetric_011),
sz_metric(new TMOP_AMetric_014a)
{
// (1-gamma) nu_11 + gamma nu_14
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual ~TMOP_AMetric_126() { delete sh_metric; delete sz_metric; }
};
/// Base class for limiting functions to be used in class TMOP_Integrator.
/** This class represents a scalar function f(x, x0, d), where x and x0 are
+106 -118
View File
@@ -331,6 +331,10 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
energy_in = nlf->GetEnergy(x);
}
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetMesh()->Dimension();
Array<int> xdofs;
DenseMatrix Jpr(dim);
// Get the local prolongation of the solution vector.
Vector x_out_loc(fes->GetVSize());
if (serial)
@@ -346,32 +350,51 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
}
#endif
// Check if the starting mesh (given by x) is inverted. Note that x hasn't
// been modified by the Newton update yet.
const double min_detT_in = ComputeMinDet(x_out_loc, *fes);
const bool untangling = (min_detT_in <= 0.0) ? true : false;
const double untangle_factor = 1.5;
if (untangling)
// Check if the starting mesh (given by x) is inverted.
// Note that x hasn't been modified by the Newton update yet.
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
{
// Needed for the line search below. The untangling metrics see this
// reference to detect deteriorations.
*min_det_ptr = untangle_factor * min_detT_in;
const int dof = fes->GetFE(i)->GetDof();
DenseMatrix dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
const IntegrationRule &irule = GetIntegrationRule(*fes->GetFE(i));
const int nsp = irule.GetNPoints();
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
}
double min_detJ_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
const bool untangling = (min_detJ_all <= 0) ? true : false;
const bool have_b = (b.Size() == Height());
Vector x_out(x.Size());
bool x_out_ok = false;
double scale = 1.0, energy_out = 0.0, min_detT_out;
const double norm_in = Norm(r);
double scale = 1.0, energy_out = 0.0;
const double norm0 = Norm(r);
const double detJ_factor = (solver_type == 1) ? 0.25 : 0.5;
// Perform the line search.
for (int i = 0; i < 12; i++)
{
// Update the mesh and get the L-vector in x_out_loc.
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
@@ -385,29 +408,47 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
}
#endif
// Check the changes in detJ.
min_detT_out = ComputeMinDet(x_out_loc, *fes);
if (untangling == false && min_detT_out < 0.0)
// Check det(Jpr) > 0.
if (!untangling)
{
// No untangling, and detJ got negative -- no good.
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= detJ_factor; continue;
}
if (untangling == true && min_detT_out < *min_det_ptr)
{
// Untangling, and detJ got even more negative -- no good.
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) decreased.\n"; }
scale *= detJ_factor; continue;
}
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
const int dof = fes->GetFE(i)->GetDof();
DenseMatrix dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
// Skip the energy and residual checks when we're untangling. The
// untangling metrics change their denominators, which can affect the
// energy and residual, so their increase/decrease is not relevant.
if (untangling) { x_out_ok = true; break; }
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
const IntegrationRule &irule = GetIntegrationRule(*fes->GetFE(i));
const int nsp = irule.GetNPoints();
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
int jac_ok_all = jac_ok;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (jac_ok_all == 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= detJ_factor; continue;
}
} // endif(!untangling)
// Check the changes in total energy.
ProcessNewState(x_out);
if (serial)
{
@@ -419,55 +460,43 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
}
#endif
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{
mfem::out << "Scale = " << scale << " Increasing energy.\n";
}
scale *= 0.5; continue;
}
// Check the changes in the Newton residual.
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm_out = Norm(r);
if (norm_out > 1.2*norm_in)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
} // end line search
if (untangling)
{
// Update the global min detJ. Untangling metrics see this min_det_ptr.
if (min_detT_out > 0.0)
{
*min_det_ptr = 0.0;
if (print_level >= 0)
{ mfem::out << "The mesh has been untangled at the used points!\n"; }
}
else { *min_det_ptr = untangle_factor * min_detT_out; }
}
if (print_level >= 0)
{
if (untangling)
{
mfem::out << "Min det(T) change: "
<< min_detT_in << " -> " << min_detT_out
<< " with " << scale << " scaling.\n";
if (energy_out > energy_in || std::isnan(energy_out) != 0)
{
scale *= 0.5;
}
else { x_out_ok = true; break; }
}
else
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Increasing energy.\n"; }
scale *= 0.5; continue;
}
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
} // endif (untangling)
} // enddo (i)
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { scale = 0.0; }
@@ -479,8 +508,8 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
// Reset the update flags of all TargetConstructors. This is done to avoid
// repeated updates of shared TargetConstructors.
// Reset the update flags of all TargetConstructors.
// This is done to avoid repeated updates of shared TargetConstructors.
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
DiscreteAdaptTC *dtc = NULL;
@@ -588,47 +617,6 @@ void TMOPNewtonSolver::UpdateDiscreteTC(const TMOP_Integrator &ti,
}
}
double TMOPNewtonSolver::ComputeMinDet(const Vector &x_loc,
const FiniteElementSpace &fes) const
{
double min_detJ = infinity();
const int NE = fes.GetNE(), dim = fes.GetMesh()->Dimension();
Array<int> xdofs;
DenseMatrix Jpr(dim);
for (int i = 0; i < NE; i++)
{
const int dof = fes.GetFE(i)->GetDof();
DenseMatrix dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
fes.GetElementVDofs(i, xdofs);
x_loc.GetSubVector(xdofs, posV);
const IntegrationRule &irule = GetIntegrationRule(*fes.GetFE(i));
const int nsp = irule.GetNPoints();
for (int j = 0; j < nsp; j++)
{
fes.GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
}
double min_detT_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
auto p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MPI_Allreduce(&min_detJ, &min_detT_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fes.GetFE(0)->GetGeomType());
min_detT_all /= Wideal.Det();
return min_detT_all;
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
-8
View File
@@ -114,9 +114,6 @@ protected:
int solver_type;
bool parallel;
// Minimum determinant over the whole mesh. Used for mesh untangling.
double *min_det_ptr = nullptr;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
// These fields are relevant for mixed meshes.
@@ -134,9 +131,6 @@ protected:
void UpdateDiscreteTC(const TMOP_Integrator &ti, const Vector &x_new) const;
double ComputeMinDet(const Vector &x_loc,
const FiniteElementSpace &fes) const;
public:
#ifdef MFEM_USE_MPI
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule, int type = 0)
@@ -156,8 +150,6 @@ public:
integ_order = order;
}
void SetMinDetPtr(double *md_ptr) { min_det_ptr = md_ptr; }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
+3 -6
View File
@@ -43,7 +43,7 @@ CeedRestrMap ceed_restr_map;
static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
{
Backend::CEED_CUDA, Backend::OCCA_CUDA, Backend::RAJA_CUDA, Backend::CUDA,
Backend::CEED_HIP, Backend::RAJA_HIP, Backend::HIP, Backend::DEBUG_DEVICE,
Backend::CEED_HIP, Backend::HIP, Backend::DEBUG_DEVICE,
Backend::OCCA_OMP, Backend::RAJA_OMP, Backend::OMP,
Backend::CEED_CPU, Backend::OCCA_CPU, Backend::RAJA_CPU, Backend::CPU
};
@@ -52,7 +52,7 @@ static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
static const char *backend_name[Backend::NUM_BACKENDS] =
{
"ceed-cuda", "occa-cuda", "raja-cuda", "cuda",
"ceed-hip", "raja-hip", "hip", "debug",
"ceed-hip", "hip", "debug",
"occa-omp", "raja-omp", "omp",
"ceed-cpu", "occa-cpu", "raja-cpu", "cpu"
};
@@ -394,8 +394,6 @@ static void RajaDeviceSetup(const int dev, int &ngpu)
{
#ifdef MFEM_USE_CUDA
if (ngpu <= 0) { DeviceSetup(dev, ngpu); }
#elif defined(MFEM_USE_HIP)
HipDeviceSetup(dev, ngpu);
#else
MFEM_CONTRACT_VAR(dev);
MFEM_CONTRACT_VAR(ngpu);
@@ -509,8 +507,7 @@ void Device::Setup(const int device)
#endif
if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
if (Allows(Backend::HIP)) { HipDeviceSetup(dev, ngpu); }
if (Allows(Backend::RAJA_CUDA) || Allows(Backend::RAJA_HIP))
{ RajaDeviceSetup(dev, ngpu); }
if (Allows(Backend::RAJA_CUDA)) { RajaDeviceSetup(dev, ngpu); }
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
if (Allows(Backend::CEED_CPU))
+10 -13
View File
@@ -46,33 +46,30 @@ struct Backend
/** @brief [device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES
and MFEM_USE_CUDA = YES. */
RAJA_CUDA = 1 << 6,
/** @brief [device] RAJA HIP backend. Enabled when MFEM_USE_RAJA = YES
and MFEM_USE_HIP = YES. */
RAJA_HIP = 1 << 7,
/** @brief [host] OCCA CPU backend: sequential execution on each MPI rank.
Enabled when MFEM_USE_OCCA = YES. */
OCCA_CPU = 1 << 8,
OCCA_CPU = 1 << 7,
/// [host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
OCCA_OMP = 1 << 9,
OCCA_OMP = 1 << 8,
/** @brief [device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES
and MFEM_USE_CUDA = YES. */
OCCA_CUDA = 1 << 10,
OCCA_CUDA = 1 << 9,
/** @brief [host] CEED CPU backend. GPU backends can still be used, but
with expensive memory transfers. Enabled when MFEM_USE_CEED = YES. */
CEED_CPU = 1 << 11,
CEED_CPU = 1 << 10,
/** @brief [device] CEED CUDA backend working together with the CUDA
backend. Enabled when MFEM_USE_CEED = YES and MFEM_USE_CUDA = YES.
NOTE: The current default libCEED CUDA backend is non-deterministic! */
CEED_CUDA = 1 << 12,
CEED_CUDA = 1 << 11,
/** @brief [device] CEED HIP backend working together with the HIP
backend. Enabled when MFEM_USE_CEED = YES and MFEM_USE_HIP = YES. */
CEED_HIP = 1 << 13,
CEED_HIP = 1 << 12,
/** @brief [device] Debug backend: host memory is READ/WRITE protected
while a device is in use. It allows to test the "device" code-path
(using separate host/device memory pools and host <-> device
transfers) without any GPU hardware. As 'DEBUG' is sometimes used
as a macro, `_DEVICE` has been added to avoid conflicts. */
DEBUG_DEVICE = 1 << 14
DEBUG_DEVICE = 1 << 13
};
/** @brief Additional useful constants. For example, the *_MASK constants can
@@ -80,14 +77,14 @@ struct Backend
enum
{
/// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
NUM_BACKENDS = 15,
NUM_BACKENDS = 14,
/// Biwise-OR of all CPU backends
CPU_MASK = CPU | RAJA_CPU | OCCA_CPU | CEED_CPU,
/// Biwise-OR of all CUDA backends
CUDA_MASK = CUDA | RAJA_CUDA | OCCA_CUDA | CEED_CUDA,
/// Biwise-OR of all HIP backends
HIP_MASK = HIP | RAJA_HIP | CEED_HIP,
HIP_MASK = HIP | CEED_HIP,
/// Biwise-OR of all OpenMP backends
OMP_MASK = OMP | RAJA_OMP | OCCA_OMP,
/// Bitwise-OR of all CEED backends
@@ -96,7 +93,7 @@ struct Backend
DEVICE_MASK = CUDA_MASK | HIP_MASK | DEBUG_DEVICE,
/// Biwise-OR of all RAJA backends
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA | RAJA_HIP,
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA,
/// Biwise-OR of all OCCA backends
OCCA_MASK = OCCA_CPU | OCCA_OMP | OCCA_CUDA
};
+44 -134
View File
@@ -86,157 +86,77 @@ void OmpWrap(const int N, HBODY &&h_body)
}
/// RAJA Cuda and Hip backends
/// RAJA Cuda backend
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
using cuda_launch_policy =
RAJA::expt::LaunchPolicy<RAJA::expt::null_launch_t, RAJA::expt::cuda_launch_t<false>>;
using cuda_teams_x =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::cuda_block_x_direct>;
using cuda_threads_z =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::cuda_thread_z_direct>;
#if RAJA_VERSION_MAJOR == 0 && RAJA_VERSION_MINOR < 12
using RAJA::statement::Segs;
#else
using RAJA::Segs;
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
using hip_launch_policy =
RAJA::expt::LaunchPolicy<RAJA::expt::null_launch_t, RAJA::expt::hip_launch_t<false>>;
using hip_teams_x =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::hip_block_x_direct>;
using hip_threads_z =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::hip_thread_z_direct>;
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
void RajaCuWrap1D(const int N, DBODY &&d_body)
void RajaCudaWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
// true denotes asynchronous kernel
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
void RajaCuWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
void RajaCudaWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
MFEM_VERIFY(N>0, "");
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<cuda_launch_policy>
(DEVICE, Resources(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE (LaunchContext ctx)
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::CudaKernelAsync<
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,G), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,BZ)),
[=] RAJA_DEVICE (const int n)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
loop<cuda_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
{
const int k = n*BZ + tz;
if (k >= N) { return; }
d_body(k);
});
});
const int k = n*BZ + threadIdx.z;
if (k >= N) { return; }
d_body(k);
});
MFEM_GPU_CHECK(cudaGetLastError());
}
template <typename DBODY>
void RajaCuWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z)
void RajaCudaWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z)
{
MFEM_VERIFY(N>0, "");
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<cuda_launch_policy>
(DEVICE, Resources(Teams(N), Threads(X, Y, Z)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
});
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::CudaKernelAsync<
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,N), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,Z)),
[=] RAJA_DEVICE (const int k) { d_body(k); });
MFEM_GPU_CHECK(cudaGetLastError());
}
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
template <const int BLOCKS = MFEM_HIP_BLOCKS, typename DBODY>
void RajaHipWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::hip_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
void RajaHipWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
MFEM_VERIFY(N>0, "");
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<hip_launch_policy>
(DEVICE, Resources(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
loop<hip_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
{
const int k = n*BZ + tz;
if (k >= N) { return; }
d_body(k);
});
});
});
MFEM_GPU_CHECK(hipGetLastError());
}
template <typename DBODY>
void RajaHipWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z)
{
MFEM_VERIFY(N>0, "");
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<hip_launch_policy>
(DEVICE, Resources(Teams(N), Threads(X, Y, Z)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(hipGetLastError());
}
#endif
/// RAJA OpenMP backend
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
#if RAJA_VERSION_MAJOR == 0 && RAJA_VERSION_MINOR < 12
using RAJA::statement::Segs;
#else
using RAJA::Segs;
#endif
template <typename HBODY>
void RajaOmpWrap(const int N, HBODY &&h_body)
{
@@ -399,19 +319,9 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::RAJA_CUDA is allowed, use it
if (Device::Allows(Backend::RAJA_CUDA))
{
if (DIM == 1) { return RajaCuWrap1D(N, d_body); }
if (DIM == 2) { return RajaCuWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaCuWrap3D(N, d_body, X, Y, Z); }
}
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
// If Backend::RAJA_HIP is allowed, use it
if (Device::Allows(Backend::RAJA_HIP))
{
if (DIM == 1) { return RajaHipWrap1D(N, d_body); }
if (DIM == 2) { return RajaHipWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaHipWrap3D(N, d_body, X, Y, Z); }
if (DIM == 1) { return RajaCudaWrap1D(N, d_body); }
if (DIM == 2) { return RajaCudaWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaCudaWrap3D(N, d_body, X, Y, Z); }
}
#endif
+1 -1
View File
@@ -125,7 +125,7 @@ void* HipMemcpyDtoDAsync(void* dst, const void *src, size_t bytes)
void* HipMemcpyDtoH(void *dst, const void *src, size_t bytes)
{
#ifdef MFEM_USE_HIP
#ifdef MFEM_TRACK_HIP_MEM
#ifdef MFEM_TRACK_HPI_MEM
mfem::out << "HipMemcpyDtoH(): copying " << bytes << " bytes from "
<< src << " to " << dst << " ... " << std::flush;
#endif
+1 -1
View File
@@ -71,7 +71,7 @@ static std::string strerror()
}
#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE || \
defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
defined(__NetBSD__) || defined(__DragonFly__) || defined(__EMSCRIPTEN__)
defined(__NetBSD__) || defined(__DragonFly__)
// XSI-compliant strerror_r()
if (strerror_r(errno, &buff[0], buff.size()) != 0)
{
-4
View File
@@ -10,13 +10,11 @@
# CONTRIBUTING.md for details.
list(APPEND SRCS
auxiliary.cpp
blockmatrix.cpp
blockoperator.cpp
blockvector.cpp
complex_operator.cpp
densemat.cpp
symmat.cpp
handle.cpp
matrix.cpp
ode.cpp
@@ -28,13 +26,11 @@ list(APPEND SRCS
)
list(APPEND HDRS
auxiliary.hpp
blockmatrix.hpp
blockoperator.hpp
blockvector.hpp
complex_operator.hpp
densemat.hpp
symmat.hpp
dtensor.hpp
handle.hpp
invariants.hpp
+3 -20
View File
@@ -30,16 +30,10 @@ int AmgXSolver::count = 0;
AMGX_resources_handle AmgXSolver::rsrc = nullptr;
AmgXSolver::AmgXSolver()
: ConvergenceCheck(false) {};
AmgXSolver::AmgXSolver(const AMGX_MODE amgxMode_, const bool verbose)
{
amgxMode = amgxMode_;
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
else { ConvergenceCheck = false;}
DefaultParameters(amgxMode, verbose);
InitSerial();
@@ -53,9 +47,6 @@ AmgXSolver::AmgXSolver(const MPI_Comm &comm,
std::string config;
amgxMode = amgxMode_;
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
else { ConvergenceCheck = false;}
DefaultParameters(amgxMode, verbose);
InitExclusiveGPU(comm);
@@ -67,9 +58,6 @@ AmgXSolver::AmgXSolver(const MPI_Comm &comm, const int nDevs,
std::string config;
amgxMode = amgxMode_;
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
else { ConvergenceCheck = false;}
DefaultParameters(amgxMode_, verbose);
InitMPITeams(comm, nDevs);
@@ -190,11 +178,6 @@ void AmgXSolver::ReadParameters(const std::string config,
configSrc = source;
}
void AmgXSolver::SetConvergenceCheck(bool setConvergenceCheck_)
{
ConvergenceCheck = setConvergenceCheck_;
}
void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
const bool verbose)
{
@@ -218,8 +201,8 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
{
amgx_config = amgx_config + ",\n"
" \"obtain_timings\": 1, \n"
" \"print_grid_stats\": 1, \n"
" \"monitor_residual\": 1, \n"
" \"print_grid_stats\": 1, \n"
" \"print_solve_stats\": 1 \n";
}
else
@@ -255,12 +238,12 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
" \"convergence\": \"RELATIVE_MAX\", \n"
" \"scope\": \"main\", \n"
" \"tolerance\": 1e-12, \n"
" \"monitor_residual\": 1, \n"
" \"norm\": \"L2\" ";
if (verbose)
{
amgx_config = amgx_config + ", \n"
" \"obtain_timings\": 1, \n"
" \"monitor_residual\": 1, \n"
" \"print_grid_stats\": 1, \n"
" \"print_solve_stats\": 1 \n";
}
@@ -901,7 +884,7 @@ void AmgXSolver::Mult(const Vector& B, Vector& X) const
AMGX_SOLVE_STATUS status;
AMGX_solver_get_status(solver, &status);
if (status != AMGX_SOLVE_SUCCESS && ConvergenceCheck)
if (status != AMGX_SOLVE_SUCCESS && amgxMode == SOLVER)
{
if (status == AMGX_SOLVE_DIVERGED)
{
+1 -7
View File
@@ -73,16 +73,13 @@ public:
/// Flags to configure AmgXSolver as a solver or preconditioner
enum AMGX_MODE {SOLVER, PRECONDITIONER};
/// Flag to check for convergence
bool ConvergenceCheck;
/**
Flags to determine whether user solver settings are defined internally in
the source code or will be read through an external JSON file.
*/
enum CONFIG_SRC {INTERNAL, EXTERNAL, UNDEFINED};
AmgXSolver();
AmgXSolver() = default;
/**
Configures AmgX with a default configuration based on the AmgX mode, and
@@ -165,9 +162,6 @@ public:
*/
void DefaultParameters(const AMGX_MODE amgxMode_, const bool verbose);
/// Add a check for convergence after applying Mult.
void SetConvergenceCheck(bool setConvergenceCheck_=true);
~AmgXSolver();
void Finalize();
-1062
View File
File diff suppressed because it is too large Load Diff
-285
View File
@@ -1,285 +0,0 @@
// Copyright (c) 2010-2020, 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_AUXILIARY
#define MFEM_AUXILIARY
#include "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "../general/tic_toc.hpp"
#include "solvers.hpp"
namespace mfem
{
// forward declarations
class Coefficient;
class MatrixCoefficient;
class ParMesh;
class ParBilinearForm;
class ParDiscreteLinearOperator;
/** @brief Auxiliary space solvers for MatrixFreeAMS preconditioner
Given an operator A and a transfer G, this will create a solver
that approximates (G^T A G)^{-1}. Used for two different
auxiliary spaces in the AMS cycle.
The produced solver is based on a low-order refined discretization
for the high-order H1 problem. */
class MatrixFreeAuxiliarySpace : public Solver
{
public:
/** @brief Pi space constructor
In the AMS framework this auxiliary space has two coefficients.
@param mesh_lor Low-order refined auxiliary mesh
@param alpha_coeff coefficient on curl-curl term (1 if null)
@param beta_coeff coefficient on mass term (1 if null)
@param beta_mcoeff matrix coefficient on mass term
@param ess_bdr attributes for essential boundaries
@param curlcurl_oper High-order operator for the system
@param pi Intentity interpolation operator
@param useAmgX_ Use AmgX instead of hypre for auxiliary solves
@param cg_iterations number of CG iterations used to invert
auxiliary system, choosing 0 means to use a
single V-cycle
*/
MatrixFreeAuxiliarySpace(
ParMesh& mesh_lor, Coefficient* alpha_coeff, Coefficient* beta_coeff,
MatrixCoefficient* beta_mcoeff,
Array<int>& ess_bdr, Operator& curlcurl_oper, Operator& pi,
#ifdef MFEM_USE_AMGX
bool useAmgX_,
#endif
int cg_iterations = 0);
// Complex Pi space constructor
MatrixFreeAuxiliarySpace(
ParMesh& mesh_lor, Coefficient* alpha_coeff, Coefficient* beta_coeff,
Coefficient* beta_imag, Coefficient* abs_beta_imag,
MatrixCoefficient* beta_mcoeff,
Array<int>& ess_bdr, Operator& curlcurl_oper, Operator *oper_complex,
Operator& pi,
#ifdef MFEM_USE_AMGX
bool useAmgX_,
#endif
int cg_iterations = 0);
/** @brief G space constructor
This has one coefficient in the AMS framework.
@param mesh_lor Low-order refined auxiliary mesh
@param beta_coeff coefficient on mass term (1 if null)
@param beta_mcoeff matrix coefficient on mass term
@param ess_bdr attributes for essential boundaries
@param curlcurl_oper High-order operator for the system
@param g Gradient interpolation operator
@param useAmgX_ Use AmgX instead of hypre for auxiliary solves
@param cg_iterations number of CG iterations used to invert
auxiliary system, choosing 0 means to
use a single V-cycle
*/
MatrixFreeAuxiliarySpace(
ParMesh& mesh_lor, Coefficient* beta_coeff,
MatrixCoefficient* beta_mcoeff, Array<int>& ess_bdr,
Operator& curlcurl_oper, Operator& g,
#ifdef MFEM_USE_AMGX
bool useAmgX_,
#endif
int cg_iterations = 1);
// Complex G space constructor
MatrixFreeAuxiliarySpace(
ParMesh& mesh_lor, Coefficient* beta_coeff, Coefficient* beta_imag,
Coefficient* abs_beta_imag,
MatrixCoefficient* beta_mcoeff, Array<int>& ess_bdr,
Operator& curlcurl_oper, Operator *oper_complex, Operator& g,
#ifdef MFEM_USE_AMGX
bool useAmgX_,
#endif
int cg_iterations = 1);
~MatrixFreeAuxiliarySpace();
void Mult(const Vector& x, Vector& y) const;
void SetOperator(const Operator& op) {}
private:
/** @brief Helper routine for constructors.
@param system_dimension is passed to HypreBoomerAMG::SetSystemsOptions
*/
void SetupAMG(int system_dimension);
void SetupVCycle();
/// inner_cg_iterations > 99 applies an exact solve here
void SetupCG(Operator& curlcurl_oper, Operator& conn,
int inner_cg_iterations);
void SetupGMRES(Operator& curlcurl_oper, Operator& conn);
void SetupPMHSS();
MPI_Comm comm;
Array<int> ess_tdof_list;
HypreParMatrix * aspacematrix;
HypreParMatrix * aspacematrix_complex;
HypreParMatrix * aspacematrix_imag;
Solver * aspacepc;
Operator* matfree;
CGSolver* cg;
GMRESSolver* gmres;
GMRESSolver* gmres_PMHSS;
Operator* aspacewrapper;
#ifdef MFEM_USE_AMGX
const bool useAmgX;
#endif
mutable int inner_aux_iterations;
const bool imagBdry;
Complex_PMHSS *PMHSS = NULL;
Array<int> offsets;
Array<int> offsets_nd;
BlockDiagonalPreconditioner *BlockDP;
BlockOperator *conn_block;
};
/** @brief Perform AMS cycle with generic Operator objects.
Most users should use MatrixFreeAMS, which wraps this. */
class GeneralAMS : public Solver
{
public:
/** @brief Constructor.
Most of these arguments just need a Mult() operation,
but pi and g also require MultTranspose() */
GeneralAMS(const Operator& curlcurl_op_,
Operator *oper_complex,
const Operator& pi_,
const Operator& gradient_,
const Operator& pispacesolver_,
const Operator& gspacesolver_,
const Operator& smoother_,
const Array<int>& ess_tdof_list_);
virtual ~GeneralAMS();
/// in principle this should set A_ = op;
void SetOperator(const Operator &op) {}
virtual void Mult(const Vector& x, Vector& y) const;
private:
const Operator& curlcurl_op;
Operator *oper_complex;
const Operator& pi;
const Operator& gradient;
const Operator& pispacesolver;
const Operator& gspacesolver;
const Operator& smoother;
const Array<int> ess_tdof_list;
void FormResidual(const Vector& rhs, const Vector& x,
Vector& residual) const;
};
/** @brief An auxiliary Maxwell solver for a high-order curl-curl
system without high-order assembly.
The auxiliary space solves are done using a low-order refined approach,
but all the interpolation operators, residuals, etc. are done in a
matrix-free manner.
See Barker and Kolev, Matrix-free preconditioning for high-order H(curl)
discretizations (https://doi.org/10.1002/nla.2348) */
class MatrixFreeAMS : public Solver
{
public:
/** @brief Construct matrix-free AMS preconditioner
@param aform BilinearForm for curl-curl problem, generally will
have a CurlCurlIntegrator and possibly a
VectorFEMassIntegrator.
@param oper Operator to precondition.
@param nd_fespace Underlying Nedelec finite element space.
@param alpha_coeff coefficient on curl-curl term in Maxwell problem
(can be null, in which case constant 1 is assumed)
@param beta_coeff (scalar) coefficient on mass term in Maxwell problem
@param beta_mcoeff (matrix) coefficient on mass term
@param ess_bdr boundary *attributes* that are marked essential. In
contrast to other MFEM cases, these are *attributes*
not dofs, because we need to apply these boundary
conditions to different bilinear forms.
@param useAmgX use AmgX (instead of hypre) for LOR problems
@param inner_pi_its number of CG iterations on auxiliary pi space,
may need more for difficult coefficients
@param inner_g_its number of CG iterations on auxiliary g space,
may need more for difficult coefficients
@param nd_smoother optional user-provided smoother for Nedelec space,
this object takes ownership and will delete.
*/
MatrixFreeAMS(ParBilinearForm& aform, Operator& oper, Operator *oper_complex,
ParFiniteElementSpace& nd_fespace, Coefficient* alpha_coeff,
Coefficient* beta_coeff, Coefficient* beta_imag,
Coefficient* abs_beta_imag, MatrixCoefficient* beta_mcoeff,
Array<int>& ess_bdr,
#ifdef MFEM_USE_AMGX
bool useAmgX = false,
#endif
int inner_pi_its = 0, int inner_g_its = 1,
Solver* nd_smoother = NULL);
~MatrixFreeAMS();
void SetOperator(const Operator &op) {}
void Mult(const Vector& x, Vector& y) const { general_ams->Mult(x, y); }
private:
GeneralAMS * general_ams;
Solver * smoother;
ParDiscreteLinearOperator * pa_grad;
OperatorPtr Gradient;
ParDiscreteLinearOperator * pa_interp;
OperatorPtr Pi;
Solver * Gspacesolver;
Solver * Pispacesolver;
ParFiniteElementSpace * h1_fespace;
ParFiniteElementSpace * h1_fespace_d;
Array<int> offsets_nd;
Array<int> offsets_vector;
Array<int> offsets_scalar;
BlockOperator *Pi_block;
BlockOperator *Gradient_block;
BlockOperator *smoother_block;
};
} // namespace mfem
#endif // MFEM_USE_MPI
#endif
-1
View File
@@ -101,7 +101,6 @@ void BlockVector::Update(Vector & data, const Array<int> & bOffsets)
{
blocks[i].MakeRef(data, blockOffsets[i], BlockSize(i));
}
MakeRef(data, 0, blockOffsets[numBlocks]);
}
void BlockVector::Update(const Array<int> &bOffsets)
+15 -19
View File
@@ -287,7 +287,10 @@ void ComplexUMFPackSolver::Init()
void ComplexUMFPackSolver::SetOperator(const Operator &op)
{
int *Ap, *Ai;
void *Symbolic;
double *Ax;
double *Az;
if (Numeric)
{
@@ -319,11 +322,10 @@ void ComplexUMFPackSolver::SetOperator(const Operator &op)
width = mat->real().Width();
MFEM_VERIFY(width == height, "not a square matrix");
const int * Ap =
mat->real().HostReadI(); // assuming real and imag have the same sparsity
const int * Ai = mat->real().HostReadJ();
const double * Ax = mat->real().HostReadData();
const double * Az = mat->imag().HostReadData();
Ap = mat->real().GetI(); // assuming real and imag have the same sparsity
Ai = mat->real().GetJ();
Ax = mat->real().GetData();
Az = mat->imag().GetData();
if (!use_long_ints)
{
@@ -393,10 +395,6 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
int n = b.Size()/2;
double * datax = x.GetData();
double * datab = b.GetData();
@@ -415,8 +413,8 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
if (!use_long_ints)
{
int status =
umfpack_zi_solve(UMFPACK_Aat, mat->real().HostReadI(), mat->real().HostReadJ(),
mat->real().HostReadData(), mat->imag().HostReadData(),
umfpack_zi_solve(UMFPACK_Aat, mat->real().GetI(), mat->real().GetJ(),
mat->real().GetData(), mat->imag().GetData(),
datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
umfpack_zi_report_info(Control, Info);
if (status < 0)
@@ -428,8 +426,8 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().HostReadData(),
mat->imag().HostReadData(),
umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().GetData(),
mat->imag().GetData(),
datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
umfpack_zl_report_info(Control, Info);
@@ -450,8 +448,6 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
int n = b.Size()/2;
double * datax = x.GetData();
double * datab = b.GetData();
@@ -471,8 +467,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
if (!use_long_ints)
{
int status =
umfpack_zi_solve(UMFPACK_A, mat->real().HostReadI(), mat->real().HostReadJ(),
mat->real().HostReadData(), mat->imag().HostReadData(),
umfpack_zi_solve(UMFPACK_A, mat->real().GetI(), mat->real().GetJ(),
mat->real().GetData(), mat->imag().GetData(),
datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
umfpack_zi_report_info(Control, Info);
if (status < 0)
@@ -484,8 +480,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().HostReadData(),
mat->imag().HostReadData(),
umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().GetData(),
mat->imag().GetData(),
datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
umfpack_zl_report_info(Control, Info);
+11 -35
View File
@@ -1279,7 +1279,6 @@ void HypreParMatrix::operator*=(double s)
static void get_sorted_rows_cols(const Array<int> &rows_cols,
Array<HYPRE_Int> &hypre_sorted)
{
rows_cols.HostRead();
hypre_sorted.SetSize(rows_cols.Size());
bool sorted = true;
for (int i = 0; i < rows_cols.Size(); i++)
@@ -1996,8 +1995,6 @@ void EliminateBC(HypreParMatrix &A, HypreParMatrix &Ae,
double *data_offd = hypre_CSRMatrixData(A_offd);
#endif
ess_dof_list.HostRead();
for (int i = 0; i < ess_dof_list.Size(); i++)
{
int r = ess_dof_list[i];
@@ -2164,7 +2161,6 @@ HypreSmoother::HypreSmoother() : Solver()
B = X = V = Z = NULL;
X0 = X1 = NULL;
fir_coeffs = NULL;
A_is_symmetric = false;
}
HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
@@ -2184,7 +2180,6 @@ HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
B = X = V = Z = NULL;
X0 = X1 = NULL;
fir_coeffs = NULL;
A_is_symmetric = false;
SetOperator(_A);
}
@@ -2472,16 +2467,6 @@ void HypreSmoother::Mult(const Vector &b, Vector &x) const
Mult(*B, *X);
}
void HypreSmoother::MultTranspose(const Vector &b, Vector &x) const
{
if (A_is_symmetric || type == 0 || type == 1 || type == 5)
{
Mult(b, x);
return;
}
mfem_error("HypreSmoother::MultTranspose (...) : undefined!\n");
}
HypreSmoother::~HypreSmoother()
{
if (B) { delete B; }
@@ -2526,14 +2511,6 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
mfem_error("HypreSolver::Mult (...) : HypreParMatrix A is missing");
return;
}
if (!iterative_mode)
{
x = 0.0;
}
b.HostRead();
x.HostReadWrite();
if (!setup_called)
{
err = SetupFcn()(*this, *A, b, x);
@@ -2549,6 +2526,10 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
setup_called = 1;
}
if (!iterative_mode)
{
x = 0.0;
}
err = SolveFcn()(*this, *A, b, x);
if (error_mode == WARN_HYPRE_ERRORS)
{
@@ -2569,7 +2550,7 @@ void HypreSolver::Mult(const Vector &b, Vector &x) const
return;
}
auto b_data = b.HostRead();
auto x_data = iterative_mode ? x.HostReadWrite() : x.HostWrite();
auto x_data = x.HostWrite();
if (B == NULL)
{
B = new HypreParVector(A->GetComm(),
@@ -2692,11 +2673,6 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
HYPRE_ParCSRMatrixGetComm(*A, &comm);
if (!iterative_mode)
{
x = 0.0;
}
if (!setup_called)
{
if (print_level > 0 && print_level < 3)
@@ -2705,8 +2681,6 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
hypre_BeginTiming(time_index);
}
b.HostRead();
x.HostReadWrite();
HYPRE_ParCSRPCGSetup(pcg_solver, *A, b, x);
setup_called = 1;
@@ -2725,8 +2699,14 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
hypre_BeginTiming(time_index);
}
if (!iterative_mode)
{
x = 0.0;
}
b.HostRead();
x.HostReadWrite();
HYPRE_ParCSRPCGSolve(pcg_solver, *A, b, x);
if (print_level > 0)
@@ -3723,9 +3703,6 @@ void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
}
x = x_coord.ParallelProject();
y = y_coord.ParallelProject();
x->HostReadWrite();
y->HostReadWrite();
if (sdim == 2)
{
z = NULL;
@@ -3734,7 +3711,6 @@ void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
else
{
z = z_coord.ParallelProject();
z->HostReadWrite();
HYPRE_AMSSetCoordinateVectors(ams, *x, *y, *z);
}
}
-12
View File
@@ -638,9 +638,6 @@ protected:
/// Combined coefficients for windowing and Chebyshev polynomials.
double* fir_coeffs;
/// A flag that indicates whether the linear system matrix A is symmetric
bool A_is_symmetric;
public:
/** Hypre smoother types:
0 = Jacobi
@@ -687,12 +684,6 @@ public:
entries in the associated matrix. */
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
/** Explicitly indicate whether the linear system matrix A is symmetric. If A
is symmetric, the smoother will also be symmetric. In this case, calling
MultTranspose will be redirected to Mult. (This is also done if the
smoother is diagonal.) By default, A is assumed to be nonsymmetric. */
void SetOperatorSymmetry(bool is_sym) { A_is_symmetric = is_sym; }
/** Set/update the associated operator. Must be called after setting the
HypreSmoother type and options. */
virtual void SetOperator(const Operator &op);
@@ -701,9 +692,6 @@ public:
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
virtual void Mult(const Vector &b, Vector &x) const;
/// Apply transpose of the smoother to relax the linear system Ax=b
virtual void MultTranspose(const Vector &b, Vector &x) const;
virtual ~HypreSmoother();
};
+22 -15
View File
@@ -62,6 +62,8 @@ protected:
const scalar_t *D; // Always points to external data or is empty
scalar_t *DaJ, *DJt, *DXt, *DYt;
scalar_t sign_detJ;
enum EvalMasks
{
HAVE_I1 = 1,
@@ -94,7 +96,8 @@ protected:
{
eval_state |= HAVE_I2b;
const scalar_t det = J[0]*J[3] - J[1]*J[2];
I2b = det;
sign_detJ = scalar_ops::sign(det);
I2b = sign_detJ*det;
}
void Eval_dI1()
{
@@ -133,10 +136,10 @@ protected:
// I2b = det(J)
// dI2b = adj(J)^T
Get_I2b();
dI2b[0] = J[3];
dI2b[1] = -J[2];
dI2b[2] = -J[1];
dI2b[3] = J[0];
dI2b[0] = sign_detJ*J[3];
dI2b[1] = -sign_detJ*J[2];
dI2b[2] = -sign_detJ*J[1];
dI2b[3] = sign_detJ*J[0];
}
void Eval_DaJ() // D adj(J) = D dI2b^t
{
@@ -513,6 +516,8 @@ protected:
const scalar_t *D; // Always points to external data or is empty
scalar_t *DaJ, *DJt, *DdI2t, *DXt, *DYt;
scalar_t sign_detJ;
enum EvalMasks
{
HAVE_I1 = 1,
@@ -580,6 +585,8 @@ protected:
eval_state |= HAVE_I3b;
I3b = J[0]*(J[4]*J[8] - J[7]*J[5]) - J[1]*(J[3]*J[8] - J[5]*J[6]) +
J[2]*(J[3]*J[7] - J[4]*J[6]);
sign_detJ = scalar_ops::sign(I3b);
I3b = sign_detJ*I3b;
}
scalar_t Get_I3b_p() // I3b^{-2/3}
{
@@ -587,7 +594,7 @@ protected:
{
eval_state |= HAVE_I3b_p;
const scalar_t i3b = Get_I3b();
I3b_p = scalar_ops::pow(i3b, -2, 3);
I3b_p = sign_detJ*scalar_ops::pow(i3b, -2, 3);
}
return I3b_p;
}
@@ -673,15 +680,15 @@ protected:
eval_state |= HAVE_dI3b;
// I3b = det(J)
// dI3b = adj(J)^T
dI3b[0] = J[4]*J[8] - J[5]*J[7]; // 0 3 6
dI3b[1] = J[5]*J[6] - J[3]*J[8]; // 1 4 7
dI3b[2] = J[3]*J[7] - J[4]*J[6]; // 2 5 8
dI3b[3] = J[2]*J[7] - J[1]*J[8];
dI3b[4] = J[0]*J[8] - J[2]*J[6];
dI3b[5] = J[1]*J[6] - J[0]*J[7];
dI3b[6] = J[1]*J[5] - J[2]*J[4];
dI3b[7] = J[2]*J[3] - J[0]*J[5];
dI3b[8] = J[0]*J[4] - J[1]*J[3];
dI3b[0] = sign_detJ*(J[4]*J[8] - J[5]*J[7]); // 0 3 6
dI3b[1] = sign_detJ*(J[5]*J[6] - J[3]*J[8]); // 1 4 7
dI3b[2] = sign_detJ*(J[3]*J[7] - J[4]*J[6]); // 2 5 8
dI3b[3] = sign_detJ*(J[2]*J[7] - J[1]*J[8]);
dI3b[4] = sign_detJ*(J[0]*J[8] - J[2]*J[6]);
dI3b[5] = sign_detJ*(J[1]*J[6] - J[0]*J[7]);
dI3b[6] = sign_detJ*(J[1]*J[5] - J[2]*J[4]);
dI3b[7] = sign_detJ*(J[2]*J[3] - J[0]*J[5]);
dI3b[8] = sign_detJ*(J[0]*J[4] - J[1]*J[3]);
}
void Eval_DZt(const scalar_t *Z, scalar_t **DZt_ptr)
{
-2
View File
@@ -24,12 +24,10 @@
#include "blockoperator.hpp"
#include "sparsesmoothers.hpp"
#include "densemat.hpp"
#include "symmat.hpp"
#include "ode.hpp"
#include "solvers.hpp"
#include "handle.hpp"
#include "invariants.hpp"
#include "auxiliary.hpp"
#ifdef MFEM_USE_AMGX
#include "amgxsolver.hpp"
-2
View File
@@ -196,8 +196,6 @@ void MUMPSSolver::SetOperator(const Operator &op)
void MUMPSSolver::Mult(const Vector &x, Vector &y) const
{
x.HostRead();
y.HostReadWrite();
#if MFEM_MUMPS_VERSION >= 530
id->nloc_rhs = x.Size();
+8 -49
View File
@@ -37,8 +37,7 @@ protected:
const Array<int> &test_tdof_list,
RectangularConstrainedOperator* &Aout);
/** @brief Returns RAP Operator of this, using input/output Prolongation matrices
@a Pi corresponds to "P", @a Po corresponds to "Rt" */
/// Returns RAP Operator of this, taking in input/output Prolongation matrices
Operator *SetupRAP(const Operator *Pi, const Operator *Po);
public:
@@ -113,11 +112,6 @@ public:
{
return GetProlongation(); // Assume square unless specialized
}
/** @brief Transpose of GetOutputRestriction, directly available in this
form to facilitate matrix-free RAP-type operators.
`NULL` means identity. */
virtual const Operator *GetOutputRestrictionTranspose() const { return NULL; }
/** @brief Restriction operator from output vectors for the operator to linear
algebra (linear system) vectors. `NULL` means identity. */
virtual const Operator *GetOutputRestriction() const
@@ -612,22 +606,23 @@ public:
using TimeDependentOperator::ImplicitSolve;
/** @brief Solve the equation:
@a k = f(@a x + @a fac0 @a k, @a dxdt + @a fac1 @a k, t), for the
@a k = f(@a x + 1/2 @a dt0^2 @a k, @a dxdt + @a dt1 @a k, t), for the
unknown @a k at the current time t.
For general F and G, the equation for @a k becomes:
F(@a x + @a fac0 @a k, @a dxdt + @a fac1 @a k, t)
= G(@a x + @a fac0 @a k, @a dxdt + @a fac1 @a k, t).
F(@a x + 1/2 @a dt0^2 @a k, @a dxdt + @a dt1 @a k, t)
= G(@a x + 1/2 @a dt0^2 @a k, @a dxdt + @a dt1 @a k, t).
The input vectors @a x and @a dxdt corresponds to time index (or cycle) n, while the
The input vector @a x corresponds to time index (or cycle) n, while the
currently set time, #t, and the result vector @a k correspond to time
index n+1.
index n+1. The time step @a dt corresponds to the time interval between
cycles n and n+1.
This method allows for the abstract implementation of some time
integration methods.
If not re-implemented, this method simply generates an error. */
virtual void ImplicitSolve(const double fac0, const double fac1,
virtual void ImplicitSolve(const double dt0, const double dt1,
const Vector &x, const Vector &dxdt, Vector &k);
@@ -696,42 +691,6 @@ public:
{ A_.Mult(x, y); y *= a_; }
};
/// General sum operator: x -> A(x)+B(x)
class SumOperator : public Operator
{
const Operator *A, *B;
bool ownA, ownB;
mutable Vector z, w;
double cA, cB;
public:
SumOperator(const Operator *A_, const Operator *B_,
bool ownA_, bool ownB_, double cA_, double cB_)
: Operator(A_->Height(), B_->Width()),
A(A_), B(B_), ownA(ownA_), ownB(ownB_), z(A_->Height()), w(A_->Width()),
cA(cA_), cB(cB_)
{
MFEM_VERIFY(A->Width() == B->Width() && A->Height() == B->Height(),
"incompatible Operators: A->Width() = " << A->Width()
<< ", B->Height() = " << B->Height());
z.UseDevice(true);
w.UseDevice(true);
}
~SumOperator()
{
if (ownA) { delete A; }
if (ownB) { delete B; }
}
virtual void Mult(const Vector &x, Vector &y) const
{ B->Mult(x, z); A->Mult(x, y); y *= cA; z *= cB; y += z;}
virtual void MultTranspose(const Vector &x, Vector &y) const
{ B->MultTranspose(x, w); A->MultTranspose(x, y); y *= cA; w *= cB; y += w;}
};
/** @brief The transpose of a given operator. Switches the roles of the methods
Mult() and MultTranspose(). */
+15 -107
View File
@@ -1556,7 +1556,6 @@ void NewtonSolver::SetOperator(const Operator &op)
width = op.Width();
MFEM_ASSERT(height == width, "square Operator is required.");
xcur.SetSize(width);
r.SetSize(width);
c.SetSize(width);
}
@@ -1616,20 +1615,9 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
break;
}
grad = &oper->GetGradient(x);
prec->SetOperator(*grad);
prec->SetOperator(oper->GetGradient(x));
if (lin_rtol_type)
{
AdaptiveLinRtolPreSolve(x, it, norm);
}
prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
if (lin_rtol_type)
{
AdaptiveLinRtolPostSolve(c, r, it, norm);
}
prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
const double c_scale = ComputeScalingFactor(x, b);
if (c_scale == 0.0)
@@ -1653,86 +1641,6 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
final_norm = norm;
}
void NewtonSolver::SetAdaptiveLinRtol(const int type,
const double rtol0,
const double rtol_max,
const double alpha,
const double gamma)
{
lin_rtol_type = type;
lin_rtol0 = rtol0;
lin_rtol_max = rtol_max;
this->alpha = alpha;
this->gamma = gamma;
}
void NewtonSolver::AdaptiveLinRtolPreSolve(const Vector &x,
const int it,
const double fnorm) const
{
// Assume that when adaptive linear solver relative tolerance is activated,
// we are working with an iterative solver.
auto iterative_solver = static_cast<IterativeSolver *>(prec);
// Adaptive linear solver relative tolerance
double eta;
// Safeguard threshold
double sg_threshold = 0.1;
if (it == 0)
{
eta = lin_rtol0;
}
else
{
if (lin_rtol_type == 1)
{
// eta = gamma * abs(||F(x1)|| - ||F(x0) + DF(x0) s0||) / ||F(x0)||
eta = gamma * abs(fnorm - lnorm_last) / fnorm_last;
}
else if (lin_rtol_type == 2)
{
// eta = gamma * (||F(x1)|| / ||F(x0)||)^alpha
eta = gamma * pow(fnorm / fnorm_last, alpha);
}
else
{
MFEM_ABORT("Unknown adaptive linear solver rtol version");
}
// Safeguard rtol from "oversolving" ?!
const double sg_eta = gamma * pow(eta_last, alpha);
if (sg_eta > sg_threshold) { eta = std::max(eta, sg_eta); }
}
eta = std::min(eta, lin_rtol_max);
iterative_solver->SetRelTol(eta);
eta_last = eta;
if (print_level >= 0)
{
mfem::out << "Eisenstat-Walker rtol = " << eta << "\n";
}
}
void NewtonSolver::AdaptiveLinRtolPostSolve(const Vector &x,
const Vector &b,
const int it,
const double fnorm) const
{
fnorm_last = fnorm;
// If version 1 is chosen, the true linear residual norm has to be computed
// and in most cases we can only retrieve the preconditioned linear residual
// norm.
if (lin_rtol_type == 1)
{
// lnorm_last = ||F(x0) + DF(x0) s0||
Vector linres(x.Size());
grad->Mult(x, linres);
linres -= b;
lnorm_last = Norm(linres);
}
}
void LBFGSSolver::Mult(const Vector &b, Vector &x) const
{
MFEM_VERIFY(oper != NULL, "the Operator is not set (use SetOperator).");
@@ -2812,7 +2720,9 @@ void UMFPackSolver::Init()
void UMFPackSolver::SetOperator(const Operator &op)
{
int *Ap, *Ai;
void *Symbolic;
double *Ax;
if (Numeric)
{
@@ -2838,9 +2748,9 @@ void UMFPackSolver::SetOperator(const Operator &op)
width = mat->Width();
MFEM_VERIFY(width == height, "not a square matrix");
const int * Ap = mat->HostReadI();
const int * Ai = mat->HostReadJ();
const double * Ax = mat->HostReadData();
Ap = mat->GetI();
Ai = mat->GetJ();
Ax = mat->GetData();
if (!use_long_ints)
{
@@ -2910,13 +2820,12 @@ void UMFPackSolver::Mult(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("UMFPackSolver::Mult : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
if (!use_long_ints)
{
int status =
umfpack_di_solve(UMFPACK_At, mat->HostReadI(), mat->HostReadJ(),
mat->HostReadData(), x, b, Numeric, Control, Info);
umfpack_di_solve(UMFPACK_At, mat->GetI(), mat->GetJ(),
mat->GetData(), x, b, Numeric, Control, Info);
umfpack_di_report_info(Control, Info);
if (status < 0)
{
@@ -2927,7 +2836,7 @@ void UMFPackSolver::Mult(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->HostReadData(), x, b,
umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->GetData(), x, b,
Numeric, Control, Info);
umfpack_dl_report_info(Control, Info);
if (status < 0)
@@ -2943,13 +2852,12 @@ void UMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("UMFPackSolver::MultTranspose : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
if (!use_long_ints)
{
int status =
umfpack_di_solve(UMFPACK_A, mat->HostReadI(), mat->HostReadJ(),
mat->HostReadData(), x, b, Numeric, Control, Info);
umfpack_di_solve(UMFPACK_A, mat->GetI(), mat->GetJ(),
mat->GetData(), x, b, Numeric, Control, Info);
umfpack_di_report_info(Control, Info);
if (status < 0)
{
@@ -2961,7 +2869,7 @@ void UMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->HostReadData(), x, b,
umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->GetData(), x, b,
Numeric, Control, Info);
umfpack_dl_report_info(Control, Info);
if (status < 0)
+1 -217
View File
@@ -406,40 +406,7 @@ void MINRES(const Operator &A, Solver &B, const Vector &b, Vector &x,
class NewtonSolver : public IterativeSolver
{
protected:
mutable Vector xcur, r, c;
mutable Operator *grad;
// Adaptive linear solver rtol variables
// Method to determine rtol, 0 means the adaptive algorithm is deactivated.
int lin_rtol_type = 0;
// rtol to use in first iteration
double lin_rtol0;
// Maximum rtol
double lin_rtol_max;
// Function norm ||F(x)|| of the previous iterate
mutable double fnorm_last = 0.0;
// Linear residual norm of the previous iterate
mutable double lnorm_last = 0.0;
// Forcing term (linear residual rtol) from the previous iterate
mutable double eta_last = 0.0;
// Eisenstat-Walker factor gamma
double gamma;
// Eisenstat-Walker factor alpha
double alpha;
/** @brief Method for the adaptive linear solver rtol invoked before the
linear solve. */
void AdaptiveLinRtolPreSolve(const Vector &x,
const int it,
const double fnorm) const;
/** @brief Method for the adaptive linear solver rtol invoked after the
linear solve. */
void AdaptiveLinRtolPostSolve(const Vector &x,
const Vector &b,
const int it,
const double fnorm) const;
mutable Vector r, c;
public:
NewtonSolver() { }
@@ -467,26 +434,6 @@ public:
/** @brief This method can be overloaded in derived classes to perform
computations that need knowledge of the newest Newton state. */
virtual void ProcessNewState(const Vector &x) const { }
const Vector &GetCurrentResidual() const { return r; }
const Vector &GetCurrentIterate() const { return xcur; }
/// Enable adaptive linear solver relative tolerance algorithm.
/** Compute a relative tolerance for the Krylov method after each nonlinear
iteration, based on the algorithm presented in [1].
The maximum linear solver relative tolerance @a rtol_max should be < 1. For
@a type 1 the parameters @a alpha and @a gamma are ignored. For @a type 2
@a alpha has to be between 0 and 1 and @a gamma between 1 and 2.
[1] Eisenstat, Stanley C., and Homer F. Walker. "Choosing the forcing terms
in an inexact Newton method."
*/
void SetAdaptiveLinRtol(const int type = 2,
const double rtol0 = 0.5,
const double rtol_max = 0.9,
const double alpha = 0.5 * (1.0 + sqrt(5.0)),
const double gamma = 1.0);
};
/** L-BFGS method for solving F(x)=b for a given operator F, by minimizing
@@ -852,169 +799,6 @@ public:
#endif // MFEM_USE_SUITESPARSE
class Complex_PMHSS : public Solver
{
public:
Complex_PMHSS(Operator *Re, Operator *Im, Solver *prec_Re, Solver *prec_Im,
double a_)
: Solver(2*Re->Height()), a(a_), A(Re, Im, false, false),
A_Re(Re, NULL, false, false),
A_Im(Im, NULL, false, false), u(2*Re->Height()), rhs(2*Re->Height()),
n(Re->Height())
{
MFEM_VERIFY(Re->Height() == Im->Height() && Re->Height() == Re->Width() &&
Im->Height() == Im->Width(), "");
MFEM_VERIFY(this->Height() == A.Height(), "");
// Create CG solver for real operator aV + A_Re in complex space.
V = useIdentityV ? (Operator*) new IdentityOperator(this->Height()) :
(Operator*) &A_Re;
// In the case V = A_Re, it is faster to use a scaled operator than a SumOperator
Operator *sumOpRe = useIdentityV ? (Operator*) new SumOperator(V, &A_Re, false,
false, a, 1.0)
: (Operator*) new ScaledOperator(&A_Re, a + 1.0);
SumOperator *sumOpIm = new SumOperator(V, &A_Im, false, false, a, 1.0);
CGSolver *cg = new CGSolver(MPI_COMM_WORLD);
cg->SetRelTol(1e-6);
cg->SetMaxIter(1000);
cg->SetPrintLevel(0);
cg->SetOperator(*sumOpRe);
cg->SetPreconditioner(*prec_Re);
cg->iterative_mode = false;
SRe = cg;
CGSolver *cgi = new CGSolver(MPI_COMM_WORLD);
cgi->SetRelTol(1e-6);
cgi->SetMaxIter(1000);
cgi->SetPrintLevel(0);
cgi->SetOperator(*sumOpIm);
if (prec_Im && useIdentityV) { cgi->SetPreconditioner(*prec_Im); }
if (!useIdentityV) { cgi->SetPreconditioner(*prec_Re); }
cgi->iterative_mode = false;
/*
// For negative definite imaginary part, but then PMHSS does not work?
MINRESSolver *cgi = new MINRESSolver(MPI_COMM_WORLD);
cgi->SetRelTol(1e-12);
cgi->SetMaxIter(1000);
cgi->SetPrintLevel(0);
cgi->SetOperator(*sumOpIm);
if (prec_Im) cgi->SetPreconditioner(*prec_Im);
*/
SIm = cgi;
}
void SetOperator(const Operator &op)
{
MFEM_VERIFY(false, "Don't call SetOperator");
}
void ComputeResidual(const Vector &b, const Vector &sol, Vector &res) const
{
A.Mult(sol, res);
res -= b;
}
void Mult(const Vector &x, Vector &y) const
{
if (!(x.Size() == Height() && y.Size() == Height()))
{
std::cout << "bug";
}
MFEM_VERIFY(x.Size() == Height() && y.Size() == Height(), "");
const double initNorm = x.Norml2();
mfem::out << "MHSS RHS norm " << initNorm << '\n';
// With V = I, use modified HSS (MHSS) from Bai, Benzi, Chen 2010.
y = 0.0;
for (int it=0; it<maxiter; ++it)
{
// Solve (aI + Re) u = (aI - i Im) y + x
if (it == 0)
{
// Optimize the first iteration, when the initial guess is y=0.
SRe->Mult(x, u);
}
else
{
A_Im.Mult(y, u); // u = Im y
// Set rhs = -i Im y = -i u
for (int j=0; j<n; ++j)
{
rhs[j] = u[n+j];
rhs[n+j] = -u[j];
}
rhs += x;
V->Mult(y, u);
rhs.Add(a, u);
SRe->Mult(rhs, u);
}
// Solve (aI + Im) y = (aI + i Re) u - i x
A_Re.Mult(u, y); // y = Re u
// Set rhs = i (Re u - x) = i (y - x)
for (int j=0; j<n; ++j)
{
rhs[j] = -(y[n+j] - x[n+j]);
rhs[n+j] = y[j] - x[j];
}
if (useIdentityV)
{
//V->Mult(u, y);
//rhs.Add(a, y);
rhs.Add(a, u);
}
else
{
// Using V = A_Re
rhs.Add(a, y);
}
SIm->Mult(rhs, y);
ComputeResidual(x, y, rhs);
const double resNorm = rhs.Norml2();
mfem::out << "MHSS iter " << it << " residual norm " << resNorm << '\n';
if (resNorm / initNorm < tol)
{
mfem::out << "MHSS converged\n";
break;
}
}
}
private:
const double a;
const int maxiter = 1;
ComplexOperator A, A_Re, A_Im;
mutable Vector u, rhs;
const int n;
const double tol = 1.0e-8;
const bool useIdentityV = false;
Operator *V = NULL;
Solver *SRe = NULL;
Solver *SIm = NULL;
};
}
#endif // MFEM_SOLVERS
+11 -15
View File
@@ -415,14 +415,10 @@ void SparseMatrix::SortColumnIndices()
return;
}
const int * Ip=HostReadI();
HostReadWriteJ();
HostReadWriteData();
Array<Pair<int,double> > row;
for (int j = 0, i = 0; i < height; i++)
{
int end = Ip[i+1];
int end = I[i+1];
row.SetSize(end - j);
for (int k = 0; k < row.Size(); k++)
{
@@ -3362,12 +3358,12 @@ SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
"number of columns of A (" << ncolsA
<< ") must equal number of rows of B (" << nrowsB << ")");
A_i = A.HostReadI();
A_j = A.HostReadJ();
A_data = A.HostReadData();
B_i = B.HostReadI();
B_j = B.HostReadJ();
B_data = B.HostReadData();
A_i = A.GetI();
A_j = A.GetJ();
A_data = A.GetData();
B_i = B.GetI();
B_j = B.GetJ();
B_data = B.GetData();
B_marker = new int[ncolsB];
@@ -3413,16 +3409,16 @@ SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
{
C = OAB;
MFEM_VERIFY(nrowsA == C->Height() && ncolsB == C->Width(),
MFEM_VERIFY(nrowsA == C -> Height() && ncolsB == C -> Width(),
"Input matrix sizes do not match output sizes"
<< " nrowsA = " << nrowsA
<< ", C->Height() = " << C->Height()
<< " ncolsB = " << ncolsB
<< ", C->Width() = " << C->Width());
// C_i = C->HostReadI(); // not used
C_j = C->HostWriteJ();
C_data = C->HostWriteData();
// C_i = C -> GetI(); // not used
C_j = C -> GetJ();
C_data = C -> GetData();
}
counter = 0;
+2 -3
View File
@@ -523,10 +523,9 @@ void SuperLUSolver::Mult( const Vector & x, Vector & y ) const
// SuperLU overwrites x with y, so copy x to y and pass that to the solve
// routine.
const double *xPtr = x.HostRead();
y = xPtr;
double * yPtr = y.HostReadWrite();
y = x;
double* yPtr = (double*)y;
int info = -1, locSize = y.Size();
// Solve the system
-109
View File
@@ -1,109 +0,0 @@
// Copyright (c) 2010-2020, 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.
// Implementation of data type DenseSymmetricMatrix
#include "symmat.hpp"
namespace mfem
{
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0)
{
data.Reset();
}
DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
{
MFEM_ASSERT(s >= 0, "invalid DenseSymmetricMatrix size: " << s);
if (s > 0)
{
data.New((s*(s+1))/2);
*this = 0.0; // init with zeroes
}
else
{
data.Reset();
}
}
void DenseSymmetricMatrix::SetSize(int s)
{
MFEM_ASSERT(s >= 0,
"invalid DenseSymmetricMatrix size: " << s);
if (Height() == s)
{
return;
}
height = s;
width = s;
const int s2 = (s*(s+1))/2;
if (s2 > data.Capacity())
{
data.Delete();
data.New(s2);
*this = 0.0; // init with zeroes
}
}
DenseSymmetricMatrix &DenseSymmetricMatrix::operator=(double c)
{
const int s = (Height()*(Height()+1))/2;
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
double &DenseSymmetricMatrix::Elem(int i, int j)
{
return (*this)(i,j);
}
const double &DenseSymmetricMatrix::Elem(int i, int j) const
{
return (*this)(i,j);
}
DenseSymmetricMatrix &DenseSymmetricMatrix::operator*=(double c)
{
int s = Height()*(Height()+1)/2;
for (int i = 0; i < s; i++)
{
data[i] *= c;
}
return *this;
}
void DenseSymmetricMatrix::Mult(const Vector &x, Vector &y) const
{
mfem_error("DenseSymmetricMatrix::Mult() not implemented!");
}
MatrixInverse *DenseSymmetricMatrix::Inverse() const
{
mfem_error("DenseSymmetricMatrix::Inverse() not implemented!");
return nullptr;
}
void DenseSymmetricMatrix::Print (std::ostream & out, int width_) const
{
mfem_error("DenseSymmetricMatrix::Print() not implemented!");
}
DenseSymmetricMatrix::~DenseSymmetricMatrix()
{
data.Delete();
}
}
-175
View File
@@ -1,175 +0,0 @@
// Copyright (c) 2010-2020, 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_SYMMETRICMAT
#define MFEM_SYMMETRICMAT
#include "../config/config.hpp"
#include "../general/globals.hpp"
#include "matrix.hpp"
namespace mfem
{
/// Dense symmetric matrix storing the upper triangular part. This class so far
/// has little functionality beyond storage.
class DenseSymmetricMatrix : public Matrix
{
private:
Memory<double> data;
public:
/** Default constructor for DenseSymmetricMatrix.
Sets data = NULL and height = width = 0. */
DenseSymmetricMatrix();
/// Creates square matrix of size s.
explicit DenseSymmetricMatrix(int s);
/// Construct a DenseSymmetricMatrix using an existing data array.
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
not delete the array. */
DenseSymmetricMatrix(double *d, int s)
: Matrix(s, s) { UseExternalData(d, s); }
/// Change the data array and the size of the DenseSymmetricMatrix.
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
not delete the data array @a d. This method should not be used with
DenseSymmetricMatrix that owns its current data array. */
void UseExternalData(double *d, int s)
{
data.Wrap(d, (s*(s+1))/2, false);
height = s; width = s;
}
/// Change the data array and the size of the DenseSymmetricMatrix.
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
not delete the new array @a d. This method will delete the current data
array, if owned. */
void Reset(double *d, int s)
{ if (OwnsData()) { data.Delete(); } UseExternalData(d, s); }
/** Clear the data array and the dimensions of the DenseSymmetricMatrix. This method
should not be used with DenseSymmetricMatrix that owns its current data array. */
void ClearExternalData() { data.Reset(); height = width = 0; }
/// Delete the matrix data array (if owned) and reset the matrix state.
void Clear()
{ if (OwnsData()) { data.Delete(); } ClearExternalData(); }
/// Change the size of the DenseSymmetricMatrix to s x s.
void SetSize(int s);
/// Returns the matrix data array.
inline double *Data() const
{ return const_cast<double*>((const double*)data);}
/// Returns the matrix data array.
inline double *GetData() const { return Data(); }
Memory<double> &GetMemory() { return data; }
const Memory<double> &GetMemory() const { return data; }
/// Return the DenseSymmetricMatrix data (host pointer) ownership flag.
inline bool OwnsData() const { return data.OwnsHostPtr(); }
/// Returns reference to a_{ij}.
inline double &operator()(int i, int j);
/// Returns constant reference to a_{ij}.
inline const double &operator()(int i, int j) const;
/// Returns reference to a_{ij}.
virtual double &Elem(int i, int j);
/// Returns constant reference to a_{ij}.
virtual const double &Elem(int i, int j) const;
/// Sets the matrix elements equal to constant c
DenseSymmetricMatrix &operator=(double c);
DenseSymmetricMatrix &operator*=(double c);
long MemoryUsage() const { return data.Capacity() * sizeof(double); }
/// Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
const double *Read(bool on_dev = true) const
{ return mfem::Read(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::Read(GetMemory(), TotalSize(), false).
const double *HostRead() const
{ return mfem::Read(data, Height()*Width(), false); }
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), on_dev).
double *Write(bool on_dev = true)
{ return mfem::Write(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), false).
double *HostWrite()
{ return mfem::Write(data, Height()*Width(), false); }
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), on_dev).
double *ReadWrite(bool on_dev = true)
{ return mfem::ReadWrite(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), false).
double *HostReadWrite()
{ return mfem::ReadWrite(data, Height()*Width(), false); }
/// Matrix vector multiplication.
virtual void Mult(const Vector &x, Vector &y) const;
/// Returns a pointer to (an approximation) of the matrix inverse.
virtual MatrixInverse *Inverse() const;
/// Prints matrix to stream out.
virtual void Print (std::ostream & out = mfem::out, int width_ = 4) const;
/// Destroys the symmetric matrix.
virtual ~DenseSymmetricMatrix();
};
// Inline methods
// The number of entries stored in rows 1,...,k is
// n + n-1 + n-2 + ... + n-k+1, where there are k terms. This equals
// kn - sum_{i=1}^{k-1} i = kn - (k-1)k/2
// This formula is used for the offset for each row.
inline double &DenseSymmetricMatrix::operator()(int i, int j)
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
if (i > j) // reverse i and j
{
return data[(j*height) - (((j-1)*j)/2) + i - j];
}
else
{
return data[(i*height) - (((i-1)*i)/2) + j - i];
}
}
inline const double &DenseSymmetricMatrix::operator()(int i, int j) const
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
if (i > j) // reverse i and j
{
return data[(j*height) - (((j-1)*j)/2) + i - j];
}
else
{
return data[(i*height) - (((i-1)*i)/2) + j - i];
}
}
} // namespace mfem
#endif
+1 -1
View File
@@ -536,7 +536,7 @@ clean: $(addsuffix /clean,$(EM_DIRS) $(TEST_DIRS))
distclean: clean config/clean doc/clean
rm -rf mfem/
INSTALL_SHARED_LIB = $(MFEM_CXX) $(MFEM_LINK_FLAGS) $(INSTALL_SOFLAGS)\
INSTALL_SHARED_LIB = $(MFEM_CXX) $(MFEM_BUILD_FLAGS) $(INSTALL_SOFLAGS)\
$(OBJECT_FILES) $(EXT_LIBS) -o $(PREFIX_LIB)/libmfem.$(SO_VER) && \
cd $(PREFIX_LIB) && ln -sf libmfem.$(SO_VER) libmfem.$(SO_EXT)
+224 -13
View File
@@ -29,6 +29,7 @@
#include <cstring>
#include <ctime>
#include <functional>
#include <unordered_set>
// Include the METIS header, if using version 5. If using METIS 4, the needed
// declarations are inlined below, i.e. no header is needed.
@@ -1049,13 +1050,6 @@ void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
*Inf2 = faces_info[Face].Elem2Inf;
}
void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
{
*Inf1 = faces_info[Face].Elem1Inf;
*Inf2 = faces_info[Face].Elem2Inf;
*NCFace = faces_info[Face].NCFace;
}
Geometry::Type Mesh::GetFaceGeometryType(int Face) const
{
switch (Dim)
@@ -5990,7 +5984,6 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
el_to_el = NULL;
// Check for empty partitionings (a "feature" in METIS)
if (nparts > 1 && NumOfElements > nparts)
{
Array< Pair<int,int> > psize(nparts);
int empty_parts;
@@ -8937,11 +8930,9 @@ void Mesh::PrintVTK(std::ostream &out)
const int *v = elements[i]->GetVertices();
const int nv = elements[i]->GetNVertices();
out << nv;
Geometry::Type geom = elements[i]->GetGeometryType();
const int *perm = (geom == Geometry::PRISM) ? vtk_prism_perm : NULL;
for (int j = 0; j < nv; j++)
{
out << ' ' << v[perm ? perm[j] : j];
out << ' ' << v[j];
}
out << '\n';
}
@@ -9269,10 +9260,9 @@ void Mesh::PrintVTU(std::ostream &out, int ref, VTKFormat format,
{
coff = coff+nv;
offset.push_back(coff);
const int *p = (geom == Geometry::PRISM) ? vtk_prism_perm : NULL;
for (int k = 0; k < nv; k++, j++)
{
WriteBinaryOrASCII(out, buf, np + RG[p ? p[j] : j], " ", format);
WriteBinaryOrASCII(out, buf, np + RG[j], " ", format);
}
if (format == VTKFormat::ASCII) { out << '\n'; }
}
@@ -10560,6 +10550,227 @@ void Mesh::RemoveInternalBoundaries()
attribs.Copy(bdr_attributes);
}
void Mesh::RemoveInternalBoundaries(const Array<int> &_keep)
{
if (NURBSext || ncmesh) { return; }
std::unordered_set<int> keep(_keep.GetData(),
_keep.GetData() + _keep.Size());
int num_bdr_elem = 0;
int new_bel_to_edge_nnz = 0;
for (int i = 0; i < GetNBE(); i++)
{
if (FaceIsInterior(GetBdrElementEdgeIndex(i)) &&
(keep.count(boundary[i]->GetAttribute()) == 0))
{
FreeElement(boundary[i]);
}
else
{
num_bdr_elem++;
if (Dim == 3)
{
new_bel_to_edge_nnz += bel_to_edge->RowSize(i);
}
}
}
if (num_bdr_elem == GetNBE()) { return; }
Array<Element *> new_boundary(num_bdr_elem);
Array<int> new_be_to_edge, new_be_to_face;
Table *new_bel_to_edge = NULL;
new_boundary.SetSize(0);
if (Dim == 2)
{
new_be_to_edge.Reserve(num_bdr_elem);
}
else if (Dim == 3)
{
new_be_to_face.Reserve(num_bdr_elem);
new_bel_to_edge = new Table;
new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
}
for (int i = 0; i < GetNBE(); i++)
{
/// if it's not interior or is in the keep boundary list
if (!FaceIsInterior(GetBdrElementEdgeIndex(i)) ||
keep.count(boundary[i]->GetAttribute()))
{
new_boundary.Append(boundary[i]);
if (Dim == 2)
{
new_be_to_edge.Append(be_to_edge[i]);
}
else if (Dim == 3)
{
int row = new_be_to_face.Size();
new_be_to_face.Append(be_to_face[i]);
int *e = bel_to_edge->GetRow(i);
int ne = bel_to_edge->RowSize(i);
int *new_e = new_bel_to_edge->GetRow(row);
for (int j = 0; j < ne; j++)
{
new_e[j] = e[j];
}
new_bel_to_edge->GetI()[row+1] = new_bel_to_edge->GetI()[row] + ne;
}
}
}
NumOfBdrElements = new_boundary.Size();
mfem::Swap(boundary, new_boundary);
if (Dim == 2)
{
mfem::Swap(be_to_edge, new_be_to_edge);
}
else if (Dim == 3)
{
mfem::Swap(be_to_face, new_be_to_face);
delete bel_to_edge;
bel_to_edge = new_bel_to_edge;
}
Array<int> attribs(num_bdr_elem);
for (int i = 0; i < attribs.Size(); i++)
{
attribs[i] = GetBdrAttribute(i);
}
attribs.Sort();
attribs.Unique();
bdr_attributes.DeleteAll();
attribs.Copy(bdr_attributes);
}
void Mesh::RemoveInternalBoundariesNotAdjacentTo(const Array<int> &_regions)
{
if (NURBSext || ncmesh) { return; }
std::unordered_set<int> regions(_regions.GetData(),
_regions.GetData() + _regions.Size());
int num_bdr_elem = 0;
int new_bel_to_edge_nnz = 0;
for (int i = 0; i < GetNBE(); i++)
{
auto faceNo = GetBdrElementEdgeIndex(i);
bool interior = FaceIsInterior(faceNo);
auto face_info = faces_info[faceNo];
auto adj = regions.count(elements[face_info.Elem1No]->GetAttribute());
if (face_info.Elem2No > 0)
adj += regions.count(elements[face_info.Elem2No]->GetAttribute());
bool remove = interior & !adj;
// if (remove)
// {
// std::cout << "remove face on: " << boundary[i]->GetAttribute()
// << " adj to ("
// << elements[face_info.Elem1No]->GetAttribute();
// if (face_info.Elem2No > 0)
// {
// std::cout << ", "
// << elements[face_info.Elem2No]->GetAttribute() << ")\n";
// }
// else
// {
// std::cout << ")\n";
// }
// }
if (remove)
{
FreeElement(boundary[i]);
}
else
{
num_bdr_elem++;
if (Dim == 3)
{
new_bel_to_edge_nnz += bel_to_edge->RowSize(i);
}
}
}
if (num_bdr_elem == GetNBE()) { return; }
Array<Element *> new_boundary(num_bdr_elem);
Array<int> new_be_to_edge, new_be_to_face;
Table *new_bel_to_edge = NULL;
new_boundary.SetSize(0);
if (Dim == 2)
{
new_be_to_edge.Reserve(num_bdr_elem);
}
else if (Dim == 3)
{
new_be_to_face.Reserve(num_bdr_elem);
new_bel_to_edge = new Table;
new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
}
for (int i = 0; i < GetNBE(); i++)
{
auto faceNo = GetBdrElementEdgeIndex(i);
bool interior = FaceIsInterior(faceNo);
auto face_info = faces_info[faceNo];
auto adj = regions.count(elements[face_info.Elem1No]->GetAttribute());
if (face_info.Elem2No > 0)
adj += regions.count(elements[face_info.Elem2No]->GetAttribute());
bool keep = !interior || adj;
if (keep)
{
new_boundary.Append(boundary[i]);
if (Dim == 2)
{
new_be_to_edge.Append(be_to_edge[i]);
}
else if (Dim == 3)
{
int row = new_be_to_face.Size();
new_be_to_face.Append(be_to_face[i]);
int *e = bel_to_edge->GetRow(i);
int ne = bel_to_edge->RowSize(i);
int *new_e = new_bel_to_edge->GetRow(row);
for (int j = 0; j < ne; j++)
{
new_e[j] = e[j];
}
new_bel_to_edge->GetI()[row+1] = new_bel_to_edge->GetI()[row] + ne;
}
}
}
NumOfBdrElements = new_boundary.Size();
mfem::Swap(boundary, new_boundary);
if (Dim == 2)
{
mfem::Swap(be_to_edge, new_be_to_edge);
}
else if (Dim == 3)
{
mfem::Swap(be_to_face, new_be_to_face);
delete bel_to_edge;
bel_to_edge = new_bel_to_edge;
}
Array<int> attribs(num_bdr_elem);
for (int i = 0; i < attribs.Size(); i++)
{
attribs[i] = GetBdrAttribute(i);
}
attribs.Sort();
attribs.Unique();
bdr_attributes.DeleteAll();
attribs.Copy(bdr_attributes);
}
void Mesh::FreeElement(Element *E)
{
#ifdef MFEM_USE_MEMALLOC
+6 -1
View File
@@ -1036,7 +1036,6 @@ public:
}
void GetFaceElements (int Face, int *Elem1, int *Elem2) const;
void GetFaceInfos (int Face, int *Inf1, int *Inf2) const;
void GetFaceInfos (int Face, int *Inf1, int *Inf2, int *NCFace) const;
Geometry::Type GetFaceGeometryType(int Face) const;
Element::Type GetFaceElementType(int Face) const;
@@ -1307,6 +1306,12 @@ public:
have two adjacent faces in 3D, or edges in 2D. */
void RemoveInternalBoundaries();
/** Remove boundary elements that lie in the interior of the mesh, except
for those with boundary attributes in `keep` */
void RemoveInternalBoundaries(const Array<int> &keep);
void RemoveInternalBoundariesNotAdjacentTo(const Array<int> &regions);
/** @brief Get the size of the i-th element relative to the perfect
reference element. */
double GetElementSize(int i, int type = 0);
-1
View File
@@ -284,7 +284,6 @@ public:
int ordering = 1);
int GetNFaceNeighbors() const { return face_nbr_group.Size(); }
int GetNFaceNeighborElements() const { return face_nbr_elements.Size(); }
int GetFaceNbrGroup(int fn) const { return face_nbr_group[fn]; }
int GetFaceNbrRank(int fn) const;
-2
View File
@@ -18,8 +18,6 @@
namespace mfem
{
const int vtk_prism_perm[6] = {0, 2, 1, 3, 5, 4};
int BarycentricToVTKTriangle(int *b, int ref)
{
// Cf. https://git.io/JvW8f
-2
View File
@@ -26,8 +26,6 @@ enum class VTKFormat
BINARY32
};
extern const int vtk_prism_perm[6];
/// Create the VTK element connectivity array for a given element geometry and
/// refinement level. Converts node numbers from MFEM to VTK ordering.
void CreateVTKElementConnectivity(Array<int> &con, Geometry::Type geom,
-4
View File
@@ -19,10 +19,6 @@ if (MFEM_USE_GSLIB)
MAIN findpts.cpp
LIBRARIES mfem)
add_mfem_miniapp(field-interp
MAIN field-interp.cpp
LIBRARIES mfem)
# Parallel apps.
if (MFEM_USE_MPI)
add_mfem_miniapp(pfindpts
-717
View File
@@ -1,717 +0,0 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
#
dimension
2
elements
18
1 3 0 1 8 7
1 3 1 2 9 8
1 3 2 3 10 9
1 3 3 4 11 10
1 3 4 5 12 11
1 3 5 6 13 12
1 3 7 8 15 14
1 3 8 9 16 15
1 3 9 10 17 16
1 3 10 11 18 17
1 3 11 12 19 18
1 3 12 13 20 19
1 3 14 15 22 21
1 3 15 16 23 22
1 3 16 17 24 23
1 3 17 18 25 24
1 3 18 19 26 25
1 3 19 20 27 26
boundary
18
4 1 0 1
4 1 1 2
4 1 2 3
4 1 3 4
4 1 4 5
4 1 5 6
2 1 22 21
2 1 23 22
2 1 24 23
2 1 25 24
2 1 26 25
2 1 27 26
4 1 7 0
4 1 14 7
4 1 21 14
4 1 6 13
4 1 13 20
4 1 20 27
vertices
28
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P4
VDim: 2
Ordering: 0
0
1
2
3
3.1666667
3.3333333
3.5
-0.33333333
0.61111111
1.5555556
2.5
2.8888889
3.2777778
3.6666667
-0.66666667
0.22222222
1.1111111
2
2.6111111
3.2222222
3.8333333
-1
-0.16666667
0.66666667
1.5
2.3333333
3.1666667
4
0.17267316
0.5
0.82732684
0.93284932
0.80555556
0.67826179
-0.17025312
0.13888889
0.4480309
-0.057557722
-0.16666667
-0.27577561
1.1726732
1.5
1.8273268
1.9232564
1.7777778
1.6322992
0.77419132
1.0833333
1.3924753
2.1726732
2.5
2.8273268
2.9136634
2.75
2.5863366
1.7186358
2.0277778
2.3369198
3.0287789
3.0833333
3.1378878
3.1187019
3.0277778
2.9368537
2.5671507
2.6944444
2.8217382
3.1954455
3.25
3.3045545
3.3237404
3.3055556
3.2873707
2.9560396
3.0833333
3.2106271
3.3621122
3.4166667
3.4712211
3.5287789
3.5833333
3.6378878
3.3449285
3.4722222
3.599516
0.54396044
0.41666667
0.2893729
-0.51317941
-0.22222222
0.068734965
-0.39089105
-0.5
-0.60910895
1.4788119
1.3333333
1.1878547
0.37570948
0.66666667
0.95762385
2.4136634
2.25
2.0863366
1.2645984
1.5555556
1.8465127
2.8409241
2.75
2.6590759
2.1055225
2.3055556
2.5055886
3.2681848
3.25
3.2318152
2.7166336
2.9166667
3.1166997
3.6954455
3.75
3.8045545
3.3277447
3.5277778
3.7278108
0.15507155
0.027777778
-0.099515992
-0.8561057
-0.58333333
-0.31056097
-0.72422439
-0.83333333
-0.94244228
1.0343675
0.88888889
0.7434103
-0.022772363
0.25
0.52277236
1.9136634
1.75
1.5863366
0.81056097
1.0833333
1.3561057
2.5631463
2.4722222
2.3812981
1.6438943
1.9166667
2.189439
3.2126293
3.1944444
3.1762596
2.4772276
2.75
3.0227724
3.8621122
3.9166667
3.9712211
3.310561
3.5833333
3.8561057
0.113459
0.4376458
0.76183261
0.0012100212
0.31944444
0.63767887
-0.11103896
0.20124309
0.51352513
1.103866
1.4280528
1.7522397
0.97343224
1.2916667
1.6099011
0.84299844
1.1552805
1.4675625
2.0942731
2.4184599
2.7426467
1.9456545
2.2638889
2.5821233
1.7970358
2.1093179
2.4215999
2.9490681
3.0161827
3.0832973
2.7979648
2.8888889
2.979813
2.6468615
2.7615951
2.8763288
3.1541065
3.2212211
3.2883357
3.0757425
3.1666667
3.2575908
2.9973785
3.1121122
3.2268458
3.359145
3.4262596
3.4933742
3.3535203
3.4444444
3.5353686
3.3478956
3.4626293
3.5773629
-0.22946729
0.076534691
0.38253667
-0.34171627
-0.041666667
0.25838293
-0.45396524
-0.15986802
0.13422919
0.7053842
1.0113862
1.3173882
0.5749504
0.875
1.1750496
0.4445166
0.73861382
1.032711
1.6402357
1.9462377
2.2522397
1.4916171
1.7916667
2.0917163
1.3429984
1.6370957
1.9311929
2.4874399
2.6272938
2.7671477
2.3363366
2.5
2.6636634
2.1852333
2.3727062
2.5601792
2.9147006
3.0545545
3.1944084
2.8363366
3
3.1636634
2.7579726
2.9454455
3.1329185
3.3419613
3.4818152
3.6216691
3.3363366
3.5
3.6636634
3.3307119
3.5181848
3.7056578
-0.57239358
-0.28457642
0.0032407353
-0.68464255
-0.40277778
-0.120913
-0.79689153
-0.52097913
-0.24506674
0.30690236
0.59471951
0.88253667
0.17646856
0.45833333
0.74019811
0.046034758
0.32194715
0.59785955
1.1861983
1.4740155
1.7618326
1.0375797
1.3194444
1.6013092
0.88896104
1.1648734
1.4407858
2.0258117
2.2384049
2.4509981
1.8747084
2.1111111
2.3475138
1.7236051
1.9838173
2.2440296
2.6752946
2.8878878
3.100481
2.5969306
2.8333333
3.069736
2.5185666
2.7787789
3.0389911
3.3247775
3.5373707
3.7499639
3.3191528
3.5555556
3.7919583
3.3135281
3.5737404
3.8339526
0
0.5
1
1.5
1
0.5
0
0.66666667
1
1.3333333
1.6666667
1.3333333
1
0.66666667
1.3333333
1.5
1.6666667
1.8333333
1.6666667
1.5
1.3333333
2
2
2
2
2
2
2
0.086336582
0.25
0.41366342
0.58633658
0.75
0.91366342
0.72422439
0.83333333
0.94244228
0.11511544
0.33333333
0.55155122
0.58633658
0.75
0.91366342
1.0575577
1.1666667
1.2757756
1.0575577
1.1666667
1.2757756
1.0863366
1.25
1.4136634
1.5287789
1.5833333
1.6378878
1.3908911
1.5
1.6091089
1.4136634
1.25
1.0863366
1.0575577
1.1666667
1.2757756
1.6091089
1.5
1.3908911
0.91366342
0.75
0.58633658
0.58633658
0.75
0.91366342
1.2757756
1.1666667
1.0575577
0.41366342
0.25
0.086336582
0.11511544
0.33333333
0.55155122
0.94244228
0.83333333
0.72422439
1.0863366
1.25
1.4136634
1.3621122
1.4166667
1.4712211
0.78178211
1
1.2182179
1.3908911
1.5
1.6091089
1.5287789
1.5833333
1.6378878
1.6954455
1.75
1.8045545
1.6954455
1.75
1.8045545
1.3908911
1.5
1.6091089
1.8045545
1.75
1.6954455
1.0863366
1.25
1.4136634
1.6378878
1.5833333
1.5287789
0.78178211
1
1.2182179
1.4712211
1.4166667
1.3621122
1.5863366
1.75
1.9136634
2
2
2
1.4484488
1.6666667
1.8848846
1.7242244
1.8333333
1.9424423
2
2
2
1.8621122
1.9166667
1.9712211
2
2
2
1.7242244
1.8333333
1.9424423
2
2
2
1.5863366
1.75
1.9136634
2
2
2
1.4484488
1.6666667
1.8848846
2
2
2
0.19648269
0.35072601
0.50496934
0.40528049
0.54166667
0.67805285
0.61407828
0.73260732
0.85113636
0.66770383
0.82194715
0.97619048
0.82194715
0.95833333
1.0947195
0.97619048
1.0947195
1.2132486
1.138925
1.2931683
1.4474116
1.2386138
1.375
1.5113862
1.3383027
1.4568317
1.5753607
1.4474116
1.2931683
1.138925
1.5113862
1.375
1.2386138
1.5753607
1.4568317
1.3383027
0.97619048
0.82194715
0.66770383
1.0947195
0.95833333
0.82194715
1.2132486
1.0947195
0.97619048
0.50496934
0.35072601
0.19648269
0.67805285
0.54166667
0.40528049
0.85113636
0.73260732
0.61407828
0.83437049
0.93405935
1.0337482
1.0431683
1.125
1.2068317
1.2519661
1.3159407
1.3799152
1.138925
1.2386138
1.3383027
1.2931683
1.375
1.4568317
1.4474116
1.5113862
1.5753607
1.4434794
1.5431683
1.6428571
1.5431683
1.625
1.7068317
1.6428571
1.7068317
1.7708063
1.6428571
1.5431683
1.4434794
1.7068317
1.625
1.5431683
1.7708063
1.7068317
1.6428571
1.3383027
1.2386138
1.138925
1.4568317
1.375
1.2931683
1.5753607
1.5113862
1.4474116
1.0337482
0.93405935
0.83437049
1.2068317
1.125
1.0431683
1.3799152
1.3159407
1.2519661
1.4722583
1.5173927
1.5625271
1.6810561
1.7083333
1.7356106
1.8898539
1.899274
1.9086941
1.6101461
1.6552805
1.7004149
1.7643894
1.7916667
1.8189439
1.9186328
1.9280528
1.9374729
1.7480339
1.7931683
1.8383027
1.8477228
1.875
1.9022772
1.9474116
1.9568317
1.9662518
1.8383027
1.7931683
1.7480339
1.9022772
1.875
1.8477228
1.9662518
1.9568317
1.9474116
1.7004149
1.6552805
1.6101461
1.8189439
1.7916667
1.7643894
1.9374729
1.9280528
1.9186328
1.5625271
1.5173927
1.4722583
1.7356106
1.7083333
1.6810561
1.9086941
1.899274
1.8898539
+54 -124
View File
@@ -33,25 +33,25 @@
//
// Sample runs:
// Adapted analytic shape:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Adapted analytic size+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// Adapted analytic shape+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
//
// Adapted discrete size:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 80 -tid 5 -ni 50 -qo 4 -nor
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 5 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Adapted discrete size; explicit combo of metrics; mixed tri/quad mesh:
// mesh-optimizer -m ../../data/square-mixed.mesh -o 2 -rs 2 -mid 2 -tid 5 -ni 200 -bnd -qo 6 -cmb 2 -nor
// Adapted discrete size+aspect_ratio:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 6 -ni 100
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 6 -ni 100 -qo 6 -ex -st 1 -nor
// Adapted discrete size+orientation (requires GSLIB):
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 36 -tid 8 -qo 4 -fd -ae 1 -nor
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 8 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd -ae 1
// Adapted discrete aspect-ratio+orientation (requires GSLIB):
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -bnd -qt 1 -qo 8 -fd -ae 1
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd -ae 1
// Adapted discrete aspect ratio (3D):
// mesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -bnd -qt 1 -qo 8
// mesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -ls 2 -li 100 -bnd -qt 1 -qo 8
//
// Adaptive limiting:
// mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5
@@ -61,33 +61,28 @@
// * mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1
//
// Blade shape:
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Blade shape with FD-based solver:
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 4 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// Blade limited shape:
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -bnd -qt 1 -qo 8 -lc 5000
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 5000
// ICF shape and equal size:
// mesh-optimizer -o 3 -mid 9 -tid 2 -ni 25 -ls 3 -art 2 -qo 5
// mesh-optimizer -o 3 -rs 0 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// ICF shape and initial size:
// mesh-optimizer -o 3 -mid 9 -tid 3 -ni 30 -ls 3 -bnd -qt 1 -qo 8
// mesh-optimizer -o 3 -rs 0 -mid 9 -tid 3 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// ICF shape:
// mesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// ICF limited shape:
// mesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8 -lc 10
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 10
// ICF combo shape + size (rings, slow convergence):
// mesh-optimizer -o 3 -mid 1 -tid 1 -ni 1000 -bnd -qt 1 -qo 8 -cmb 1
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 1000 -ls 2 -li 100 -bnd -qt 1 -qo 8 -cmb 1
// Mixed tet / cube / hex mesh with limiting:
// mesh-optimizer -m ../../data/fichera-mixed-p2.mesh -o 4 -rs 1 -mid 301 -tid 1 -fix-bnd -qo 6 -nor -lc 0.25
// 3D pinched sphere shape (the mesh is in the mfem/data GitHub repository):
// * mesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -mid 303 -tid 1 -ni 20 -li 500 -fix-bnd
// * mesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -rs 0 -mid 303 -tid 1 -ni 20 -ls 2 -li 500 -fix-bnd
// 2D non-conforming shape and equal size:
// mesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -bnd -qt 1 -qo 8
//
// 2D untangling:
// mesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1
// 3D untangling (the mesh is in the mfem/data GitHub repository):
// * mesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1
//
// mesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -112,9 +107,8 @@ int main(int argc, char *argv[])
int quad_type = 1;
int quad_order = 8;
int solver_type = 0;
int solver_iter = 20;
int solver_iter = 10;
double solver_rtol = 1e-10;
int solver_art_type = 0;
int lin_solver = 2;
int max_lin_iter = 100;
bool move_bnd = true;
@@ -138,38 +132,26 @@ int main(int argc, char *argv[])
"Random perturbation scaling factor.");
args.AddOption(&metric_id, "-mid", "--metric-id",
"Mesh optimization metric:\n\t"
"T-metrics\n\t"
"1 : |T|^2 -- 2D shape\n\t"
"2 : 0.5|T|^2/tau-1 -- 2D shape (condition number)\n\t"
"7 : |T-T^-t|^2 -- 2D shape+size\n\t"
"9 : tau*|T-T^-t|^2 -- 2D shape+size\n\t"
"14 : |T-I|^2 -- 2D shape+size+orientation\n\t"
"14: 0.5*(1-cos(theta_A - theta_W) -- 2D Sh+Sz+Alignment\n\t"
"22 : 0.5(|T|^2-2*tau)/(tau-tau_0) -- 2D untangling\n\t"
"50 : 0.5|T^tT|^2/tau^2-1 -- 2D shape\n\t"
"55 : (tau-1)^2 -- 2D size\n\t"
"56 : 0.5(sqrt(tau)-1/sqrt(tau))^2 -- 2D size\n\t"
"58 : |T^tT|^2/(tau^2)-2*|T|^2/tau+2 -- 2D shape\n\t"
"77 : 0.5(tau-1/tau)^2 -- 2D size\n\t"
"80 : (1-gamma)mu_2 + gamma mu_77 -- 2D shape+size\n\t"
"85 : |T-|T|/sqrt(2)I|^2 -- 2D shape+orientation\n\t"
"98 : (1/tau)|T-I|^2 -- 2D shape+size+orientation\n\t"
// "211: (tau-1)^2-tau+sqrt(tau^2) -- 2D untangling\n\t"
// "252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
"211: (tau-1)^2-tau+sqrt(tau^2) -- 2D untangling\n\t"
"252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
"301: (|T||T^-1|)/3-1 -- 3D shape\n\t"
"302: (|T|^2|T^-1|^2)/9-1 -- 3D shape\n\t"
"303: (|T|^2)/3*tau^(2/3)-1 -- 3D shape\n\t"
//"311: (tau-1)^2-tau+sqrt(tau^2+eps)-- 3D untangling\n\t"
"313: (|T|^2)(tau-tau0)^(-2/3)/3 -- 3D untangling\n\t"
"315: (tau-1)^2 -- 3D size\n\t"
"316: 0.5(sqrt(tau)-1/sqrt(tau))^2 -- 3D size\n\t"
"321: |T-T^-t|^2 -- 3D shape+size\n\t"
// "352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling\n\t"
"A-metrics\n\t"
"11 : (1/4*alpha)|A-(adjA)^T(W^TW)/omega|^2 -- 2D shape\n\t"
"36 : (1/alpha)|A-W|^2 -- 2D shape+size+orientation\n\t"
"107: (1/2*alpha)|A-|A|/|W|W|^2 -- 2D shape+orientation\n\t"
"126: (1-gamma)nu_11 + gamma*nu_14a -- 2D shape+size\n\t"
);
"352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling");
args.AddOption(&target_id, "-tid", "--target-id",
"Target (ideal element) type:\n\t"
"1: Ideal shape, unit size\n\t"
@@ -193,25 +175,15 @@ int main(int argc, char *argv[])
"Maximum number of Newton iterations.");
args.AddOption(&solver_rtol, "-rtol", "--newton-rel-tolerance",
"Relative tolerance for the Newton solver.");
args.AddOption(&solver_art_type, "-art", "--adaptive-rel-tol",
"Type of adaptive relative linear solver tolerance:\n\t"
"0: None (default)\n\t"
"1: Eisenstat-Walker type 1\n\t"
"2: Eisenstat-Walker type 2");
args.AddOption(&lin_solver, "-ls", "--lin-solver",
"Linear solver:\n\t"
"0: l1-Jacobi\n\t"
"1: CG\n\t"
"2: MINRES\n\t"
"3: MINRES + Jacobi preconditioner\n\t"
"4: MINRES + l1-Jacobi preconditioner");
"Linear solver: 0 - l1-Jacobi, 1 - CG, 2 - MINRES.");
args.AddOption(&max_lin_iter, "-li", "--lin-iter",
"Maximum number of iterations in the linear solve.");
args.AddOption(&move_bnd, "-bnd", "--move-boundary", "-fix-bnd",
"--fix-boundary",
"Enable motion along horizontal and vertical boundaries.");
args.AddOption(&combomet, "-cmb", "--combo-type",
"Combination of metrics options:\n\t"
"Combination of metrics options:"
"0: Use single metric\n\t"
"1: Shape + space-dependent size given analytically\n\t"
"2: Shape + adapted size given discretely; shared target");
@@ -343,40 +315,28 @@ int main(int argc, char *argv[])
TMOP_QualityMetric *metric = NULL;
switch (metric_id)
{
// T-metrics
case 1: metric = new TMOP_Metric_001; break;
case 2: metric = new TMOP_Metric_002; break;
case 7: metric = new TMOP_Metric_007; break;
case 9: metric = new TMOP_Metric_009; break;
case 14: metric = new TMOP_Metric_014; break;
case 14: metric = new TMOP_Metric_SSA2D; break;
case 22: metric = new TMOP_Metric_022(tauval); break;
case 50: metric = new TMOP_Metric_050; break;
case 55: metric = new TMOP_Metric_055; break;
case 56: metric = new TMOP_Metric_056; break;
case 58: metric = new TMOP_Metric_058; break;
case 77: metric = new TMOP_Metric_077; break;
case 80: metric = new TMOP_Metric_080(0.5); break;
case 85: metric = new TMOP_Metric_085; break;
case 98: metric = new TMOP_Metric_098; break;
// case 211: metric = new TMOP_Metric_211; break;
// case 252: metric = new TMOP_Metric_252(tauval); break;
case 211: metric = new TMOP_Metric_211; break;
case 252: metric = new TMOP_Metric_252(tauval); break;
case 301: metric = new TMOP_Metric_301; break;
case 302: metric = new TMOP_Metric_302; break;
case 303: metric = new TMOP_Metric_303; break;
// case 311: metric = new TMOP_Metric_311; break;
case 313: metric = new TMOP_Metric_313(tauval); break;
case 315: metric = new TMOP_Metric_315; break;
case 316: metric = new TMOP_Metric_316; break;
case 321: metric = new TMOP_Metric_321; break;
// case 352: metric = new TMOP_Metric_352(tauval); break;
// A-metrics
case 11: metric = new TMOP_AMetric_011; break;
case 36: metric = new TMOP_AMetric_036; break;
case 107: metric = new TMOP_AMetric_107a; break;
case 126: metric = new TMOP_AMetric_126(0.9); break;
default:
cout << "Unknown metric_id: " << metric_id << endl;
return 3;
case 352: metric = new TMOP_Metric_352(tauval); break;
default: cout << "Unknown metric_id: " << metric_id << endl; return 3;
}
TargetConstructor::TargetType target_t;
TargetConstructor *target_c = NULL;
@@ -385,7 +345,7 @@ int main(int argc, char *argv[])
FiniteElementSpace ind_fes(mesh, &ind_fec);
FiniteElementSpace ind_fesv(mesh, &ind_fec, dim);
GridFunction size(&ind_fes), aspr(&ind_fes), disc(&ind_fes), ori(&ind_fes);
GridFunction aspr3d(&ind_fesv);
GridFunction aspr3d(&ind_fesv), size3d(&ind_fesv);
switch (target_id)
{
case 1: target_t = TargetConstructor::IDEAL_SHAPE_UNIT_SIZE; break;
@@ -443,14 +403,14 @@ int main(int argc, char *argv[])
#endif
}
// Diffuse the interface
//Diffuse the interface
DiffuseField(disc,2);
// Get partials with respect to x and y of the grid function
//Get partials with respect to x and y of the grid function
disc.GetDerivative(1,0,d_x);
disc.GetDerivative(1,1,d_y);
// Compute the squared magnitude of the gradient
//Compute the squared magnitude of the gradient
for (int i = 0; i < size.Size(); i++)
{
size(i) = std::pow(d_x(i),2)+std::pow(d_y(i),2);
@@ -557,7 +517,7 @@ int main(int argc, char *argv[])
#endif
}
if (metric_id == 14 || metric_id == 36)
if (metric_id == 14)
{
ConstantCoefficient ind_coeff(0.1*0.1);
size.ProjectCoefficient(ind_coeff);
@@ -702,40 +662,6 @@ int main(int argc, char *argv[])
}
else { a.AddDomainIntegrator(he_nlf_integ); }
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = mesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(fespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = mesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
cout << "Minimum det(J) of the original mesh is " << tauval << endl;
if (tauval < 0.0 && metric_id != 22 && metric_id != 211 && metric_id != 252
&& metric_id != 311 && metric_id != 313 && metric_id != 352)
{
MFEM_ABORT("The input mesh is inverted! Try an untangling metric.");
}
if (tauval < 0.0)
{
MFEM_VERIFY(target_t == TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
"Untangling is supported only for ideal targets.");
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fespace->GetFE(0)->GetGeomType());
tauval /= Wideal.Det();
// Slightly below minJ0 to avoid div by 0.
tauval -= 0.01 * h0.Min();
}
const double init_energy = a.GetGridFunctionEnergy(x);
// Visualize the starting mesh and metric values.
@@ -804,7 +730,7 @@ int main(int argc, char *argv[])
// 14. As we use the Newton method to solve the resulting nonlinear system,
// here we setup the linear solver for the system's Jacobian.
Solver *S = NULL, *S_prec = NULL;
Solver *S = NULL;
const double linsol_rtol = 1e-12;
if (lin_solver == 0)
{
@@ -825,16 +751,27 @@ int main(int argc, char *argv[])
minres->SetMaxIter(max_lin_iter);
minres->SetRelTol(linsol_rtol);
minres->SetAbsTol(0.0);
if (verbosity_level > 2) { minres->SetPrintLevel(1); }
minres->SetPrintLevel(verbosity_level == 2 ? 3 : -1);
if (lin_solver == 3 || lin_solver == 4)
{
S_prec = new DSmoother((lin_solver == 3) ? 0 : 1, 1.0, 1);
minres->SetPreconditioner(*S_prec);
}
minres->SetPrintLevel(verbosity_level >= 2 ? 3 : -1);
S = minres;
}
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = mesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(fespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = mesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
cout << "Minimum det(J) of the original mesh is " << tauval << endl;
tauval -= 0.01 * h0.Min(); // Slightly below minJ0 to avoid div by 0.
// Perform the nonlinear optimization.
const IntegrationRule &ir =
irules->Get(fespace->GetFE(0)->GetGeomType(), quad_order);
@@ -846,15 +783,9 @@ int main(int argc, char *argv[])
// Specify linear solver when we use a Newton-based solver.
solver.SetPreconditioner(*S);
}
// For untangling, the solver will update the min det(T) values.
if (tauval < 0.0) { solver.SetMinDetPtr(&tauval); }
solver.SetMaxIter(solver_iter);
solver.SetRelTol(solver_rtol);
solver.SetAbsTol(0.0);
if (solver_art_type > 0)
{
solver.SetAdaptiveLinRtol(solver_art_type, 0.5, 0.9);
}
solver.SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
solver.SetOperator(a);
solver.Mult(b, x.GetTrueVector());
@@ -922,7 +853,6 @@ int main(int argc, char *argv[])
}
// 19. Free the used memory.
delete S_prec;
delete S;
delete target_c2;
delete metric2;
+2 -2
View File
@@ -114,7 +114,7 @@ public:
{
Vector pos(3);
T.Transform(ip, pos);
if (metric != 14 && metric != 36 && metric != 85)
if (metric != 14 && metric != 85)
{
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
@@ -129,7 +129,7 @@ public:
K(1, 0) = 0.0;
K(1, 1) = 1.0;
}
else if (metric == 14 || metric == 36) // Size + Alignment
else if (metric == 14) // Size + Alignment
{
const double xc = pos(0), yc = pos(1);
double theta = M_PI * yc * (1.0 - yc) * cos(2 * M_PI * xc);
+58 -131
View File
@@ -33,25 +33,25 @@
//
// Sample runs:
// Adapted analytic shape:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Adapted analytic size+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// Adapted analytic shape+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
//
// Adapted discrete size:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 80 -tid 5 -ni 50 -qo 4 -nor
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 5 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Adapted discrete size; explicit combo of metrics; mixed tri/quad mesh:
// mpirun -np 4 pmesh-optimizer -m ../../data/square-mixed.mesh -o 2 -rs 2 -mid 2 -tid 5 -ni 200 -bnd -qo 6 -cmb 2 -nor
// Adapted discrete size+aspect_ratio:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 6 -ni 100
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 6 -ni 100 -qo 6 -ex -st 1 -nor
// Adapted discrete size+orientation (requires GSLIB):
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 36 -tid 8 -qo 4 -fd -ae 1 -nor
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 8 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd -ae 1
// Adapted discrete aspect_ratio+orientation (requires GSLIB):
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -bnd -qt 1 -qo 8 -fd -ae 1
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd -ae 1
// Adapted discrete aspect ratio (3D):
// mpirun -np 4 pmesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -ls 2 -li 100 -bnd -qt 1 -qo 8
//
// Adaptive limiting:
// mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5
@@ -61,33 +61,27 @@
// * mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1
//
// Blade shape:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Blade shape with FD-based solver:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 4 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// Blade limited shape:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -bnd -qt 1 -qo 8 -lc 5000
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 5000
// ICF shape and equal size:
// mpirun -np 4 pmesh-optimizer -o 3 -mid 9 -tid 2 -ni 25 -ls 3 -art 2 -qo 5
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// ICF shape and initial size:
// mpirun -np 4 pmesh-optimizer -o 3 -mid 9 -tid 3 -ni 30 -ls 3 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 9 -tid 3 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// ICF shape:
// mpirun -np 4 pmesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// ICF limited shape:
// mpirun -np 4 pmesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8 -lc 10
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 10
// ICF combo shape + size (rings, slow convergence):
// mpirun -np 4 pmesh-optimizer -o 3 -mid 1 -tid 1 -ni 1000 -bnd -qt 1 -qo 8 -cmb 1
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 1000 -ls 2 -li 100 -bnd -qt 1 -qo 8 -cmb 1
// Mixed tet / cube / hex mesh with limiting:
// mpirun -np 4 pmesh-optimizer -m ../../data/fichera-mixed-p2.mesh -o 4 -rs 1 -mid 301 -tid 1 -fix-bnd -qo 6 -nor -lc 0.25
// 3D pinched sphere shape (the mesh is in the mfem/data GitHub repository):
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -mid 303 -tid 1 -ni 20 -li 500 -fix-bnd
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -rs 0 -mid 303 -tid 1 -ni 20 -ls 2 -li 500 -fix-bnd
// 2D non-conforming shape and equal size:
// mpirun -np 4 pmesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -bnd -qt 1 -qo 8
//
// 2D untangling:
// mpirun -np 4 pmesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1
// 3D untangling (the mesh is in the mfem/data GitHub repository):
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1
//
// mpirun -np 4 pmesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -119,9 +113,8 @@ int main (int argc, char *argv[])
int quad_type = 1;
int quad_order = 8;
int solver_type = 0;
int solver_iter = 20;
int solver_iter = 10;
double solver_rtol = 1e-10;
int solver_art_type = 0;
int lin_solver = 2;
int max_lin_iter = 100;
bool move_bnd = true;
@@ -147,38 +140,25 @@ int main (int argc, char *argv[])
"Random perturbation scaling factor.");
args.AddOption(&metric_id, "-mid", "--metric-id",
"Mesh optimization metric:\n\t"
"T-metrics\n\t"
"1 : |T|^2 -- 2D shape\n\t"
"2 : 0.5|T|^2/tau-1 -- 2D shape (condition number)\n\t"
"7 : |T-T^-t|^2 -- 2D shape+size\n\t"
"9 : tau*|T-T^-t|^2 -- 2D shape+size\n\t"
"14 : |T-I|^2 -- 2D shape+size+orientation\n\t"
"22 : 0.5(|T|^2-2*tau)/(tau-tau_0) -- 2D untangling\n\t"
"50 : 0.5|T^tT|^2/tau^2-1 -- 2D shape\n\t"
"55 : (tau-1)^2 -- 2D size\n\t"
"56 : 0.5(sqrt(tau)-1/sqrt(tau))^2 -- 2D size\n\t"
"58 : |T^tT|^2/(tau^2)-2*|T|^2/tau+2 -- 2D shape\n\t"
"77 : 0.5(tau-1/tau)^2 -- 2D size\n\t"
"80 : (1-gamma)mu_2 + gamma mu_77 -- 2D shape+size\n\t"
"85 : |T-|T|/sqrt(2)I|^2 -- 2D shape+orientation\n\t"
"98 : (1/tau)|T-I|^2 -- 2D shape+size+orientation\n\t"
// "211: (tau-1)^2-tau+sqrt(tau^2+eps) -- 2D untangling\n\t"
// "252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
"211: (tau-1)^2-tau+sqrt(tau^2) -- 2D untangling\n\t"
"252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
"301: (|T||T^-1|)/3-1 -- 3D shape\n\t"
"302: (|T|^2|T^-1|^2)/9-1 -- 3D shape\n\t"
"303: (|T|^2)/3*tau^(2/3)-1 -- 3D shape\n\t"
// "311: (tau-1)^2-tau+sqrt(tau^2+eps)-- 3D untangling\n\t"
"313: (|T|^2)(tau-tau0)^(-2/3)/3 -- 3D untangling\n\t"
"315: (tau-1)^2 -- 3D size\n\t"
"316: 0.5(sqrt(tau)-1/sqrt(tau))^2 -- 3D size\n\t"
"321: |T-T^-t|^2 -- 3D shape+size\n\t"
// "352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling\n\t"
"A-metrics\n\t"
"11 : (1/4*alpha)|A-(adjA)^T(W^TW)/omega|^2 -- 2D shape\n\t"
"36 : (1/alpha)|A-W|^2 -- 2D shape+size+orientation\n\t"
"107: (1/2*alpha)|A-|A|/|W|W|^2 -- 2D shape+orientation\n\t"
"126: (1-gamma)nu_11 + gamma*nu_14a -- 2D shape+size\n\t"
);
"352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling");
args.AddOption(&target_id, "-tid", "--target-id",
"Target (ideal element) type:\n\t"
"1: Ideal shape, unit size\n\t"
@@ -202,25 +182,15 @@ int main (int argc, char *argv[])
"Maximum number of Newton iterations.");
args.AddOption(&solver_rtol, "-rtol", "--newton-rel-tolerance",
"Relative tolerance for the Newton solver.");
args.AddOption(&solver_art_type, "-art", "--adaptive-rel-tol",
"Type of adaptive relative linear solver tolerance:\n\t"
"0: None (default)\n\t"
"1: Eisenstat-Walker type 1\n\t"
"2: Eisenstat-Walker type 2");
args.AddOption(&lin_solver, "-ls", "--lin-solver",
"Linear solver:\n\t"
"0: l1-Jacobi\n\t"
"1: CG\n\t"
"2: MINRES\n\t"
"3: MINRES + Jacobi preconditioner\n\t"
"4: MINRES + l1-Jacobi preconditioner");
"Linear solver: 0 - l1-Jacobi, 1 - CG, 2 - MINRES.");
args.AddOption(&max_lin_iter, "-li", "--lin-iter",
"Maximum number of iterations in the linear solve.");
args.AddOption(&move_bnd, "-bnd", "--move-boundary", "-fix-bnd",
"--fix-boundary",
"Enable motion along horizontal and vertical boundaries.");
args.AddOption(&combomet, "-cmb", "--combo-type",
"Combination of metrics options:\n\t"
"Combination of metrics options:"
"0: Use single metric\n\t"
"1: Shape + space-dependent size given analytically\n\t"
"2: Shape + adapted size given discretely; shared target");
@@ -373,37 +343,27 @@ int main (int argc, char *argv[])
TMOP_QualityMetric *metric = NULL;
switch (metric_id)
{
// T-metrics
case 1: metric = new TMOP_Metric_001; break;
case 2: metric = new TMOP_Metric_002; break;
case 7: metric = new TMOP_Metric_007; break;
case 9: metric = new TMOP_Metric_009; break;
case 14: metric = new TMOP_Metric_014; break;
case 14: metric = new TMOP_Metric_SSA2D; break;
case 22: metric = new TMOP_Metric_022(tauval); break;
case 50: metric = new TMOP_Metric_050; break;
case 55: metric = new TMOP_Metric_055; break;
case 56: metric = new TMOP_Metric_056; break;
case 58: metric = new TMOP_Metric_058; break;
case 77: metric = new TMOP_Metric_077; break;
case 80: metric = new TMOP_Metric_080(0.5); break;
case 85: metric = new TMOP_Metric_085; break;
case 98: metric = new TMOP_Metric_098; break;
// case 211: metric = new TMOP_Metric_211; break;
// case 252: metric = new TMOP_Metric_252(tauval); break;
case 211: metric = new TMOP_Metric_211; break;
case 252: metric = new TMOP_Metric_252(tauval); break;
case 301: metric = new TMOP_Metric_301; break;
case 302: metric = new TMOP_Metric_302; break;
case 303: metric = new TMOP_Metric_303; break;
// case 311: metric = new TMOP_Metric_311; break;
case 313: metric = new TMOP_Metric_313(tauval); break;
case 315: metric = new TMOP_Metric_315; break;
case 316: metric = new TMOP_Metric_316; break;
case 321: metric = new TMOP_Metric_321; break;
// case 352: metric = new TMOP_Metric_352(tauval); break;
// A-metrics
case 11: metric = new TMOP_AMetric_011; break;
case 36: metric = new TMOP_AMetric_036; break;
case 107: metric = new TMOP_AMetric_107a; break;
case 126: metric = new TMOP_AMetric_126(0.9); break;
case 352: metric = new TMOP_Metric_352(tauval); break;
default:
if (myid == 0) { cout << "Unknown metric_id: " << metric_id << endl; }
return 3;
@@ -415,7 +375,7 @@ int main (int argc, char *argv[])
ParFiniteElementSpace ind_fes(pmesh, &ind_fec);
ParFiniteElementSpace ind_fesv(pmesh, &ind_fec, dim);
ParGridFunction size(&ind_fes), aspr(&ind_fes), disc(&ind_fes), ori(&ind_fes);
ParGridFunction aspr3d(&ind_fesv);
ParGridFunction aspr3d(&ind_fesv), size3d(&ind_fesv);
switch (target_id)
{
@@ -453,7 +413,7 @@ int main (int argc, char *argv[])
target_c = tc;
break;
}
case 6: // material indicator 2D
case 6: //material indicator 2D
{
ParGridFunction d_x(&ind_fes), d_y(&ind_fes);
@@ -473,14 +433,14 @@ int main (int argc, char *argv[])
MFEM_ABORT("MFEM is not built with GSLIB.");
#endif
}
// Diffuse the interface
//Diffuse the interface
DiffuseField(disc,2);
// Get partials with respect to x and y of the grid function
//Get partials with respect to x and y of the grid function
disc.GetDerivative(1,0,d_x);
disc.GetDerivative(1,1,d_y);
// Compute the squared magnitude of the gradient
//Compute the squared magnitude of the gradient
for (int i = 0; i < size.Size(); i++)
{
size(i) = std::pow(d_x(i),2)+std::pow(d_y(i),2);
@@ -593,7 +553,7 @@ int main (int argc, char *argv[])
#endif
}
if (metric_id == 14 || metric_id == 36)
if (metric_id == 14)
{
ConstantCoefficient ind_coeff(0.1*0.1);
size.ProjectCoefficient(ind_coeff);
@@ -743,46 +703,6 @@ int main (int argc, char *argv[])
}
else { a.AddDomainIntegrator(he_nlf_integ); }
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = pmesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(pfespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = pmesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
double minJ0;
MPI_Allreduce(&tauval, &minJ0, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval = minJ0;
if (myid == 0)
{ cout << "Minimum det(J) of the original mesh is " << tauval << endl; }
if (tauval < 0.0 && metric_id != 22 && metric_id != 211 && metric_id != 252
&& metric_id != 311 && metric_id != 313 && metric_id != 352)
{
MFEM_ABORT("The input mesh is inverted! Try an untangling metric.");
}
if (tauval < 0.0)
{
MFEM_VERIFY(target_t == TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
"Untangling is supported only for ideal targets.");
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(pfespace->GetFE(0)->GetGeomType());
tauval /= Wideal.Det();
double h0min = h0.Min(), h0min_all;
MPI_Allreduce(&h0min, &h0min_all, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
// Slightly below minJ0 to avoid div by 0.
tauval -= 0.01 * h0min_all;
}
const double init_energy = a.GetParGridFunctionEnergy(x);
// Visualize the starting mesh and metric values.
@@ -851,7 +771,7 @@ int main (int argc, char *argv[])
// 15. As we use the Newton method to solve the resulting nonlinear system,
// here we setup the linear solver for the system's Jacobian.
Solver *S = NULL, *S_prec = NULL;
Solver *S = NULL;
const double linsol_rtol = 1e-12;
if (lin_solver == 0)
{
@@ -872,19 +792,33 @@ int main (int argc, char *argv[])
minres->SetMaxIter(max_lin_iter);
minres->SetRelTol(linsol_rtol);
minres->SetAbsTol(0.0);
if (verbosity_level > 2) { minres->SetPrintLevel(1); }
else { minres->SetPrintLevel(verbosity_level == 2 ? 3 : -1); }
if (lin_solver == 3 || lin_solver == 4)
{
HypreSmoother *hs = new HypreSmoother;
hs->SetType((lin_solver == 3) ? HypreSmoother::Jacobi
: HypreSmoother::l1Jacobi, 1);
S_prec = hs;
minres->SetPreconditioner(*S_prec);
}
minres->SetPrintLevel(verbosity_level >= 2 ? 3 : -1);
S = minres;
}
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = pmesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(pfespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = pmesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
double minJ0;
MPI_Allreduce(&tauval, &minJ0, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval = minJ0;
if (myid == 0)
{ cout << "Minimum det(J) of the original mesh is " << tauval << endl; }
double h0min = h0.Min(), h0min_all;
MPI_Allreduce(&h0min, &h0min_all, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval -= 0.01 * h0min_all; // Slightly below minJ0 to avoid div by 0.
// Perform the nonlinear optimization.
const IntegrationRule &ir =
irules->Get(pfespace->GetFE(0)->GetGeomType(), quad_order);
@@ -896,15 +830,9 @@ int main (int argc, char *argv[])
// Specify linear solver when we use a Newton-based solver.
solver.SetPreconditioner(*S);
}
// For untangling, the solver will update the min det(T) values.
if (tauval < 0.0) { solver.SetMinDetPtr(&tauval); }
solver.SetMaxIter(solver_iter);
solver.SetRelTol(solver_rtol);
solver.SetAbsTol(0.0);
if (solver_art_type > 0)
{
solver.SetAdaptiveLinRtol(solver_art_type, 0.5, 0.9);
}
solver.SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
solver.SetOperator(a);
solver.Mult(b, x.GetTrueVector());
@@ -983,7 +911,6 @@ int main (int argc, char *argv[])
}
// 20. Free the used memory.
delete S_prec;
delete S;
delete target_c2;
delete metric2;
-5
View File
@@ -34,11 +34,6 @@ if (MFEM_USE_MPI)
${NAVIER_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(navier_kovasznay_vs
MAIN navier_kovasznay_vs.cpp
${NAVIER_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(navier_tgv
MAIN navier_tgv.cpp
${NAVIER_COMMON_FILES}
+1 -1
View File
@@ -24,7 +24,7 @@ MFEM_LIB_FILE = mfem_is_not_built
NAVIER_COMMON_SRC = navier_solver.cpp ortho_solver.cpp
NAVIER_COMMON_OBJ = $(NAVIER_COMMON_SRC:.cpp=.o)
PAR_MINIAPPS = navier_mms navier_kovasznay navier_kovasznay_vs navier_tgv navier_shear navier_3dfoc
PAR_MINIAPPS = navier_mms navier_kovasznay navier_tgv navier_shear navier_3dfoc
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS =
-305
View File
@@ -1,305 +0,0 @@
// Copyright (c) 2010-2020, 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.
//
// Navier Kovasznay example with variable time step
//
// Solve for the steady Kovasznay flow at Re = 40 defined by
//
// u = [1 - exp(L * x) * cos(2 * pi * y),
// L / (2 * pi) * exp(L * x) * sin(2 * pi * y)],
//
// p = 1/2 * (1 - exp(2 * L * x)),
//
// with L = Re/2 - sqrt(Re^2/4 + 4 * pi^2).
//
// The problem domain is set up like this
//
// +-------------+
// | |
// | |
// | |
// | |
// Inflow -> | | -> Outflow
// | |
// | |
// | |
// | |
// | |
// +-------------+
//
// and Dirichlet boundary conditions are applied for the velocity on every
// boundary. The problem, although steady state, is time integrated up to the
// final time and the solution is compared with the known exact solution.
//
// Additionally, this example shows the usage of variable time steps with
// Navier. A basic sample algorithm for determining the next time step is
// provided based on a CFL restriction on the velocity.
#include "navier_solver.hpp"
#include <fstream>
using namespace mfem;
using namespace navier;
struct s_NavierContext
{
int ser_ref_levels = 1;
int order = 6;
double kinvis = 1.0 / 40.0;
double t_final = 10 * 0.001;
double dt = 0.001;
double reference_pressure = 0.0;
double reynolds = 1.0 / kinvis;
double lam = 0.5 * reynolds
- sqrt(0.25 * reynolds * reynolds + 4.0 * M_PI * M_PI);
bool pa = true;
bool ni = false;
bool visualization = false;
bool checkres = false;
} ctx;
void vel_kovasznay(const Vector &x, double t, Vector &u)
{
double xi = x(0);
double yi = x(1);
u(0) = 1.0 - exp(ctx.lam * xi) * cos(2.0 * M_PI * yi);
u(1) = ctx.lam / (2.0 * M_PI) * exp(ctx.lam * xi) * sin(2.0 * M_PI * yi);
}
double pres_kovasznay(const Vector &x, double t)
{
double xi = x(0);
return 0.5 * (1.0 - exp(2.0 * ctx.lam * xi)) + ctx.reference_pressure;
}
int main(int argc, char *argv[])
{
MPI_Session mpi(argc, argv);
OptionsParser args(argc, argv);
args.AddOption(&ctx.ser_ref_levels,
"-rs",
"--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&ctx.order,
"-o",
"--order",
"Order (degree) of the finite elements.");
args.AddOption(&ctx.dt, "-dt", "--time-step", "Time step.");
args.AddOption(&ctx.t_final, "-tf", "--final-time", "Final time.");
args.AddOption(&ctx.pa,
"-pa",
"--enable-pa",
"-no-pa",
"--disable-pa",
"Enable partial assembly.");
args.AddOption(&ctx.ni,
"-ni",
"--enable-ni",
"-no-ni",
"--disable-ni",
"Enable numerical integration rules.");
args.AddOption(&ctx.visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(
&ctx.checkres,
"-cr",
"--checkresult",
"-no-cr",
"--no-checkresult",
"Enable or disable checking of the result. Returns -1 on failure.");
args.Parse();
if (!args.Good())
{
if (mpi.Root())
{
args.PrintUsage(mfem::out);
}
return 1;
}
if (mpi.Root())
{
args.PrintOptions(mfem::out);
}
Mesh *mesh = new Mesh(2, 4, Element::QUADRILATERAL, false, 1.5, 2.0);
mesh->EnsureNodes();
GridFunction *nodes = mesh->GetNodes();
*nodes -= 0.5;
for (int i = 0; i < ctx.ser_ref_levels; ++i)
{
mesh->UniformRefinement();
}
if (mpi.Root())
{
std::cout << "Number of elements: " << mesh->GetNE() << std::endl;
}
auto *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
// Create the flow solver.
NavierSolver flowsolver(pmesh, ctx.order, ctx.kinvis);
flowsolver.EnablePA(ctx.pa);
flowsolver.EnableNI(ctx.ni);
// Set the initial condition.
ParGridFunction *u_ic = flowsolver.GetCurrentVelocity();
VectorFunctionCoefficient u_excoeff(pmesh->Dimension(), vel_kovasznay);
u_ic->ProjectCoefficient(u_excoeff);
FunctionCoefficient p_excoeff(pres_kovasznay);
// Add Dirichlet boundary conditions to velocity space restricted to
// selected attributes on the mesh.
Array<int> attr(pmesh->bdr_attributes.Max());
attr = 1;
flowsolver.AddVelDirichletBC(vel_kovasznay, attr);
double t = 0.0;
double dt = ctx.dt;
double t_final = ctx.t_final;
bool last_step = false;
flowsolver.Setup(dt);
double err_u = 0.0;
double err_p = 0.0;
ParGridFunction *u_next_gf = nullptr;
ParGridFunction *u_gf = nullptr;
ParGridFunction *p_gf = nullptr;
ParGridFunction p_ex_gf(flowsolver.GetCurrentPressure()->ParFESpace());
GridFunctionCoefficient p_ex_gf_coeff(&p_ex_gf);
double cfl_max = 0.8;
double cfl_tol = 1e-4;
for (int step = 0; !last_step; ++step)
{
if (t + dt >= t_final - dt / 2)
{
last_step = true;
}
// Take a provisional step
flowsolver.Step(t, dt, step, true);
// Retrieve the computed provisional velocity
u_next_gf = flowsolver.GetProvisionalVelocity();
// Compute the CFL based on the provisional velocity
double cfl = flowsolver.ComputeCFL(*u_next_gf, dt);
double error_est = cfl / (cfl_max + cfl_tol);
if (error_est >= 1.0)
{
// Reject the time step
if (mpi.Root())
{
std::cout
<< "Step reached maximum CFL, retrying with smaller step size..."
<< std::endl;
}
dt *= 0.5;
step -= 1;
}
else
{
// Accept the time step
t += dt;
// Predict new step size
double fac_safety = 2.0;
double eta = pow(1.0 / (fac_safety * error_est), 1.0 / (1.0 + 3.0));
double fac_min = 0.1;
double fac_max = 1.4;
dt = dt * std::min(fac_max, std::max(fac_min, eta));
// Queue new time step in the history array
flowsolver.UpdateTimestepHistory(dt);
}
u_gf = flowsolver.GetCurrentVelocity();
p_gf = flowsolver.GetCurrentPressure();
u_excoeff.SetTime(t);
p_excoeff.SetTime(t);
// Remove mean value from exact pressure solution.
p_ex_gf.ProjectCoefficient(p_excoeff);
flowsolver.MeanZero(p_ex_gf);
err_u = u_gf->ComputeL2Error(u_excoeff);
err_p = p_gf->ComputeL2Error(p_ex_gf_coeff);
if (mpi.Root())
{
printf("%5s %8s %8s %8s %11s %11s\n",
"Order",
"CFL",
"Time",
"dt",
"err_u",
"err_p");
printf("%5.2d %8.2E %.2E %.2E %.5E %.5E err\n",
ctx.order,
cfl,
t,
dt,
err_u,
err_p);
fflush(stdout);
}
}
if (ctx.visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "parallel " << mpi.WorldSize() << " " << mpi.WorldRank()
<< "\n";
sol_sock << "solution\n" << *pmesh << *u_ic << std::flush;
}
flowsolver.PrintTimingData();
// Test if the result for the test run is as expected.
if (ctx.checkres)
{
double tol_u = 1e-6;
double tol_p = 1e-5;
if (err_u > tol_u || err_p > tol_p)
{
if (mpi.Root())
{
mfem::out << "Result has a larger error than expected."
<< std::endl;
}
return -1;
}
}
delete pmesh;
return 0;
}
+55 -99
View File
@@ -49,8 +49,6 @@ NavierSolver::NavierSolver(ParMesh *mesh, int order, double kin_vis)
un.SetSize(vfes_truevsize);
un = 0.0;
un_next.SetSize(vfes_truevsize);
un_next = 0.0;
unm1.SetSize(vfes_truevsize);
unm1 = 0.0;
unm2.SetSize(vfes_truevsize);
@@ -78,8 +76,6 @@ NavierSolver::NavierSolver(ParMesh *mesh, int order, double kin_vis)
un_gf.SetSpace(vfes);
un_gf = 0.0;
un_next_gf.SetSpace(vfes);
un_next_gf = 0.0;
Lext_gf.SetSpace(vfes);
curlu_gf.SetSpace(vfes);
@@ -292,14 +288,7 @@ void NavierSolver::Setup(double dt)
HInv->SetRelTol(rtol_hsolve);
HInv->SetMaxIter(200);
// If the initial condition was set, it has to be aligned with dependent
// Vectors and GridFunctions
un_gf.GetTrueDofs(un);
un_next = un;
un_next_gf.SetFromTrueDofs(un_next);
// Set initial time step in the history array
dthist[0] = dt;
if (filter_alpha != 0.0)
{
@@ -319,64 +308,52 @@ void NavierSolver::Setup(double dt)
sw_setup.Stop();
}
void NavierSolver::UpdateTimestepHistory(double dt)
{
// Rotate values in time step history
dthist[2] = dthist[1];
dthist[1] = dthist[0];
dthist[0] = dt;
// Rotate values in nonlinear extrapolation history
Nunm2 = Nunm1;
Nunm1 = Nun;
// Rotate values in solution history
unm2 = unm1;
unm1 = un;
// Update the current solution and corresponding GridFunction
un_next_gf.GetTrueDofs(un_next);
un = un_next;
un_gf.SetFromTrueDofs(un);
}
void NavierSolver::Step(double &time, double dt, int cur_step, bool provisional)
void NavierSolver::Step(double &time, double dt, int cur_step)
{
sw_step.Start();
SetTimeIntegrationCoefficients(cur_step);
time += dt;
// Set current time for velocity Dirichlet boundary conditions.
// Set current time for velocity dirichlet boundary conditions.
for (auto &vel_dbc : vel_dbcs)
{
vel_dbc.coeff->SetTime(time + dt);
vel_dbc.coeff->SetTime(time);
}
// Set current time for pressure Dirichlet boundary conditions.
// Set current time for pressure dirichlet boundary conditions.
for (auto &pres_dbc : pres_dbcs)
{
pres_dbc.coeff->SetTime(time + dt);
pres_dbc.coeff->SetTime(time);
}
H_bdfcoeff.constant = bd0 / dt;
H_form->Update();
H_form->Assemble();
H_form->FormSystemMatrix(vel_ess_tdof, H);
SetTimeIntegrationCoefficients(cur_step);
HInv->SetOperator(*H);
if (partial_assembly)
if (cur_step <= 2)
{
delete HInvPC;
Vector diag_pa(vfes->GetTrueVSize());
H_form->AssembleDiagonal(diag_pa);
HInvPC = new OperatorJacobiSmoother(diag_pa, vel_ess_tdof);
HInv->SetPreconditioner(*HInvPC);
H_bdfcoeff.constant = bd0 / dt;
H_form->Update();
H_form->Assemble();
H_form->FormSystemMatrix(vel_ess_tdof, H);
if (partial_assembly)
{
HInv->SetOperator(*H);
delete HInvPC;
Vector diag_pa(vfes->GetTrueVSize());
H_form->AssembleDiagonal(diag_pa);
HInvPC = new OperatorJacobiSmoother(diag_pa, vel_ess_tdof);
HInv->SetPreconditioner(*HInvPC);
}
else
{
HInv->SetOperator(*H);
}
}
// Extrapolated f^{n+1}.
for (auto &accel_term : accel_terms)
{
accel_term.coeff->SetTime(time + dt);
accel_term.coeff->SetTime(time);
}
f_form->Assemble();
@@ -402,6 +379,10 @@ void NavierSolver::Step(double &time, double dt, int cur_step, bool provisional)
ab3_ * d_Nunm2[i];);
}
// Rotate the solutions from previous time steps.
Nunm2 = Nunm1;
Nunm1 = Nun;
// Fext = M^{-1} (F(u^{n}) + f^{n+1})
MvInv->Mult(Fext, tmp1);
iter_mvsolve = MvInv->GetNumIterations();
@@ -522,41 +503,35 @@ void NavierSolver::Step(double &time, double dt, int cur_step, bool provisional)
Mv->Mult(Fext, tmp1);
resu.Add(1.0, tmp1);
// un_next_gf = un_gf;
for (auto &vel_dbc : vel_dbcs)
{
un_next_gf.ProjectBdrCoefficient(*vel_dbc.coeff, vel_dbc.attr);
un_gf.ProjectBdrCoefficient(*vel_dbc.coeff, vel_dbc.attr);
}
vfes->GetRestrictionMatrix()->MultTranspose(resu, resu_gf);
// Rotate solutions from previous time steps.
unm2 = unm1;
unm1 = un;
Vector X2, B2;
if (partial_assembly)
{
auto *HC = H.As<ConstrainedOperator>();
EliminateRHS(*H_form, *HC, vel_ess_tdof, un_next_gf, resu_gf, X2, B2, 1);
EliminateRHS(*H_form, *HC, vel_ess_tdof, un_gf, resu_gf, X2, B2, 1);
}
else
{
H_form->FormLinearSystem(vel_ess_tdof, un_next_gf, resu_gf, H, X2, B2, 1);
H_form->FormLinearSystem(vel_ess_tdof, un_gf, resu_gf, H, X2, B2, 1);
}
sw_hsolve.Start();
HInv->Mult(B2, X2);
sw_hsolve.Stop();
iter_hsolve = HInv->GetNumIterations();
res_hsolve = HInv->GetFinalNorm();
H_form->RecoverFEMSolution(X2, resu_gf, un_next_gf);
H_form->RecoverFEMSolution(X2, resu_gf, un_gf);
un_next_gf.GetTrueDofs(un_next);
// If the current time step is not provisional, accept the computed solution
// and update the time step history by default.
if (!provisional)
{
UpdateTimestepHistory(dt);
time += dt;
}
un_gf.GetTrueDofs(un);
if (filter_alpha != 0.0)
{
@@ -1017,24 +992,7 @@ void NavierSolver::AddAccelTerm(VecFuncT *f, Array<int> &attr)
void NavierSolver::SetTimeIntegrationCoefficients(int step)
{
// Maxmium BDF order to use at current time step
// step + 1 <= order <= max_bdf_order
int bdf_order = std::min(step + 1, max_bdf_order);
// Ratio of time step history at dt(t_{n}) - dt(t_{n-1})
double rho1 = 0.0;
// Ratio of time step history at dt(t_{n-1}) - dt(t_{n-2})
double rho2 = 0.0;
rho1 = dthist[0] / dthist[1];
if (bdf_order == 3)
{
rho2 = dthist[1] / dthist[2];
}
if (step == 0 && bdf_order == 1)
if (step == 0)
{
bd0 = 1.0;
bd1 = -1.0;
@@ -1044,27 +1002,25 @@ void NavierSolver::SetTimeIntegrationCoefficients(int step)
ab2 = 0.0;
ab3 = 0.0;
}
else if (step >= 1 && bdf_order == 2)
else if (step == 1)
{
bd0 = (1.0 + 2.0 * rho1) / (1.0 + rho1);
bd1 = -(1.0 + rho1);
bd2 = pow(rho1, 2.0) / (1.0 + rho1);
bd0 = 3.0 / 2.0;
bd1 = -4.0 / 2.0;
bd2 = 1.0 / 2.0;
bd3 = 0.0;
ab1 = 1.0 + rho1;
ab2 = -rho1;
ab1 = 2.0;
ab2 = -1.0;
ab3 = 0.0;
}
else if (step >= 2 && bdf_order == 3)
else if (step == 2)
{
bd0 = 1.0 + rho1 / (1.0 + rho1)
+ (rho2 * rho1) / (1.0 + rho2 * (1 + rho1));
bd1 = -1.0 - rho1 - (rho2 * rho1 * (1.0 + rho1)) / (1.0 + rho2);
bd2 = pow(rho1, 2.0) * (rho2 + 1.0 / (1.0 + rho1));
bd3 = -(pow(rho2, 3.0) * pow(rho1, 2.0) * (1.0 + rho1))
/ ((1.0 + rho2) * (1.0 + rho2 + rho2 * rho1));
ab1 = ((1.0 + rho1) * (1.0 + rho2 * (1.0 + rho1))) / (1.0 + rho2);
ab2 = -rho1 * (1.0 + rho2 * (1.0 + rho1));
ab3 = (pow(rho2, 2.0) * rho1 * (1.0 + rho1)) / (1.0 + rho2);
bd0 = 11.0 / 6.0;
bd1 = -18.0 / 6.0;
bd2 = 9.0 / 6.0;
bd3 = -2.0 / 6.0;
ab1 = 3.0;
ab2 = -3.0;
ab3 = 1.0;
}
}
+6 -41
View File
@@ -100,7 +100,7 @@ public:
/**
* This implementation of a transient incompressible Navier Stokes solver uses
* the non-dimensionalized formulation. The coupled momentum and
* incompressibility equations are decoupled using the split scheme described in
* incompressibilty equations are decoupled using the split scheme described in
* [1]. This leads to three solving steps.
*
* 1. An extrapolation step for all nonlinear terms which are treated
@@ -160,31 +160,7 @@ public:
void Setup(double dt);
/// Compute solution at the next time step t+dt.
/**
* This method can be called with the default value @a provisional which
* always accepts the computed time step by automatically calling
* UpdateTimestepHistory.
*
* If @a provisional is set to true, the solution at t+dt is not accepted
* automatically and the application code has to call UpdateTimestepHistory
* and update the @a time variable accordingly.
*
* The application code can check the provisional step by retrieving the
* GridFunction with the method GetProvisionalVelocity. If the check fails,
* it is possible to retry the step with a different time step by not
* calling UpdateTimestepHistory and calling this method with the previous
* @a time and @a cur_step.
*
* The method and parameter choices are based on [1].
*
* [1] D. Wang, S.J. Ruuth (2008) Variable step-size implicit-explicit
* linear multistep methods for time-dependent partial differential
* equations
*/
void Step(double &time, double dt, int cur_step, bool provisional = false);
/// Return a pointer to the provisional velocity ParGridFunction.
ParGridFunction *GetProvisionalVelocity() { return &un_next_gf; }
void Step(double &time, double dt, int cur_step);
/// Return a pointer to the current velocity ParGridFunction.
ParGridFunction *GetCurrentVelocity() { return &un_gf; }
@@ -262,12 +238,6 @@ public:
*/
void MeanZero(ParGridFunction &v);
/// Rotate entries in the time step and solution history arrays.
void UpdateTimestepHistory(double dt);
/// Set the maximum order to use for the BDF method.
void SetMaxBDFOrder(int maxbdforder) { max_bdf_order = maxbdforder; };
/// Compute CFL
double ComputeCFL(ParGridFunction &u, double dt);
@@ -285,7 +255,7 @@ public:
void SetFilterAlpha(double a) { filter_alpha = a; }
protected:
/// Print information about the Navier version.
/// Print informations about the Navier version.
void PrintInfo();
/// Update the EXTk/BDF time integration coefficient.
@@ -293,8 +263,7 @@ protected:
* Depending on which time step the computation is in, the EXTk/BDF time
* integration coefficients have to be set accordingly. This allows
* bootstrapping with a BDF scheme of order 1 and increasing the order each
* following time step, up to order 3 (or whichever order is set in
* SetMaxBDFOrder).
* following time step, up to order 3.
*/
void SetTimeIntegrationCoefficients(int step);
@@ -387,14 +356,12 @@ protected:
Solver *HInvPC = nullptr;
CGSolver *HInv = nullptr;
Vector fn, un, un_next, unm1, unm2, Nun, Nunm1, Nunm2, Fext, FText, Lext,
resu;
Vector fn, un, unm1, unm2, Nun, Nunm1, Nunm2, Fext, FText, Lext, resu;
Vector tmp1;
Vector pn, resp, FText_bdr, g_bdr;
ParGridFunction un_gf, un_next_gf, curlu_gf, curlcurlu_gf, Lext_gf, FText_gf,
resu_gf;
ParGridFunction un_gf, curlu_gf, curlcurlu_gf, Lext_gf, FText_gf, resu_gf;
ParGridFunction pn_gf, resp_gf;
@@ -415,9 +382,7 @@ protected:
// Bookkeeping for acceleration (forcing) terms.
std::vector<AccelTerm_T> accel_terms;
int max_bdf_order = 3;
int cur_step = 0;
std::vector<double> dthist = {0.0, 0.0, 0.0};
// BDFk/EXTk coefficients.
double bd0 = 0.0;

Some files were not shown because too many files have changed in this diff Show More