Compare commits

..
Author SHA1 Message Date
Tucker Babcock 4adfa0fc86 updting io benchmark 2020-05-05 15:44:44 -04:00
Tucker Babcock 857a24f6c4 Merge branch 'PCFinalProject' of github.com:mfem/mfem into PCFinalProject 2020-05-04 11:14:22 -07:00
Tucker Babcock 1ae22c7c69 adding io benchmark 2020-05-04 11:13:42 -07:00
Tucker Babcock 161ebff2a1 Merge branch 'PCFinalProject' of https://github.com/mfem/mfem into PCFinalProject 2020-05-04 12:42:18 -04:00
Tucker Babcock 3548f2cb83 adding num ranks printing 2020-05-04 12:42:13 -04:00
Tucker Babcock 41a7730048 adding barriers ahead of timings and averaging timing over all ranks 2020-05-04 09:40:57 -07:00
Tucker Babcock b638fb8960 adding all of the operator testing to one file 2020-05-03 22:02:26 -07:00
Tucker Babcock dc80f42710 Merge branch 'PCFinalProject' of https://github.com/mfem/mfem into PCFinalProject 2020-05-04 00:51:51 -04:00
Tucker Babcock 4414a3fc01 adding test to mfem examples 2020-05-04 00:50:16 -04:00
Tucker Babcock 2f683f80fa Merge branch 'mpiio-gf-dev' into PCFinalProject 2020-04-30 14:09:33 -07:00
Tucker Babcock 6ea2f7bf55 Merge branch 'mpiio-gf-dev' of github.com:mfem/mfem into mpiio-gf-dev 2020-04-30 14:06:16 -07:00
Tucker Babcock 581cafa7a7 updating documentation 2020-04-30 14:06:10 -07:00
Tucker Babcock c5bab73f9a Merge branch 'mpiio-gf-dev' into PCFinalProject 2020-04-30 15:45:12 -04:00
Tucker Babcock b02eb71967 adding number of files printing control to example 1 2020-04-30 15:39:19 -04:00
Tucker Babcock d236571e4a cleaned up code in pgridfunc and added printing to example two. 2020-04-28 21:04:11 -07:00
Tucker Babcock 8d444d7f92 ordering by nodes appears to work now as well 2020-04-28 16:28:07 -07:00
Tucker Babcock 71937096f8 ordering by vdim works with high order 2020-04-28 16:26:30 -07:00
Tucker Babcock 4e0978cf3b can save and load files correctly for p = 1, errors otherwise. 2020-04-28 15:10:12 -07:00
Tucker Babcock e52fdd205a initial commit adding MPI-IO writing of GridFunction supporting writing to arbitrary number of files. Reading support to come 2020-04-27 22:41:28 -07:00
153 changed files with 4456 additions and 15187 deletions
+6 -3
View File
@@ -11,6 +11,8 @@
language: cpp
sudo: false
stages:
- checks
- tests
@@ -368,10 +370,8 @@ script:
# Compiler
- if [ $MPI == "YES" ]; then
export MYCXX=mpic++;
export MAKE_CXX_FLAG=MPICXX=$MYCXX;
else
export MYCXX="$CXX";
export MAKE_CXX_FLAG=CXX=$MYCXX;
fi
# Print the compiler version
@@ -384,9 +384,12 @@ script:
if [ "$CODECOV" == "YES" ]; then
CPPFLAGS="--coverage -g";
fi;
if [ "$CXX" == "clang++" ]; then
export MFEM_PERF_SW=clang;
fi
# Configure the library
- make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG $MAKE_CXX_FLAG
- make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG MFEM_CXX="$MYCXX"
MFEM_MPI_NP=$NPROCS CPPFLAGS="$CPPFLAGS"
# Show the configuration
- make info
+5 -55
View File
@@ -23,21 +23,8 @@ Meshing improvements
Hessian for r-adaptivity using discrete fields, and allows use of skewness
and orientation based metrics.
- Added support for r-adaptivity with more than one discrete field. This allows
the user to specify different discrete functions for controlling the
size, aspect-ratio, orientation, and skew of elements in the mesh.
Performance improvements
------------------------
- Added support for explicit vectorization in the high-performance templated
code, which can now take advantage of specific intrinsics classes on the
following architectures:
- x86 (SSE/AVX/AVX2/AVX512),
- Power8 & Power9 (VSX),
- BG/Q (QPX).
These are now enabled by default, and can be disabled with MFEM_USE_SIMD=NO.
See the new file linalg/simd.hpp and the new directory linalg/simd.
Improved GPU capabilities
-------------------------
- Added support for Chebyshev accelerated polynomial smoother on GPU.
Discretization improvements
@@ -48,24 +35,6 @@ Discretization improvements
- Added support for simplices in GSLIB-FindPoints.
- Added support for H1 and L2 element matrix assembly in the mass, convection,
diffusion, transpose, and the face DG trace integrators. This is compatible
with GPU device execution and is illustrated in Example 9/9p, see the option
'-ea'. When enabled, this level of assembly stores independent dense matrices
for the elements, and independent dense matrices for the faces in the DG case.
- Added new partial assembly kernels for H(div) bilinear forms, as well as
VectorFEDivergenceIntegrator.
- Improved the documentation of the GridFunction GetValue and GetVectorValue
methods. Expanded the GetValue and GetVectorValue methods which accept an
ElementTransformation argument to support evaluation on boundary elements
and, in the continuous field case, arbitrary mesh edges and faces.
- Added new coefficient and vector coefficient classes for QuadratureFunctions.
Additionaly, new LinearForm integrators were also added which make use of
these new QuadratureFunction coefficient classes.
Linear and nonlinear solvers
----------------------------
- Added power method to iteratively estimate the largest eigenvalue and the
@@ -74,17 +43,6 @@ Linear and nonlinear solvers
- Added initial support for h- and p-multigrid solvers and preconditioners for
matrix-based and matrix-free discretizations with basic GPU capability.
- Added a new IterativeSolverMonitor class that allows to monitor the residual
and solution during the solving process of an IterativeSolver after every
iteration.
- Block arrays of parallel matrices can now be merged into a single parallel
matrix with the function HypreParMatrixFromBlocks. This could be useful for
solving block systems with parallel direct solvers such as STRUMPACK.
- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
solver can work with non-SPD preconditioner B.
New and updated examples and miniapps
-------------------------------------
- Added a new example, Example 25/25p, to demonstrate the use of a Perfectly
@@ -107,12 +65,6 @@ New and updated examples and miniapps
- Added a new meshing miniapp, Minimal Surface, which solves Plateau's problem:
the Dirichlet problem for the minimal surface equation.
- Added partial assembly support to examples 4/4p and 5/5p, with diagonal
preconditioning.
- Added a new test problem in example 24/24p, demonstrating a mixed bilinear
form for H(div) and L_2, with partial assembly support.
Improved testing
----------------
- Added a GitLab pipeline that automates PR testing on supercomputing systems
@@ -122,17 +74,15 @@ Improved testing
Miscellaneous
-------------
- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
solver can work with non-SPD preconditioner B.
- Added support for ADIOS2 for parallel I/O with ParaView visualization. The
classes adios2stream and ADIOS2DataCollection are introduced in mfem as the
interfaces to generate ADIOS2 Binary Pack (BP4) directory datasets for the
entire spatial and temporal data. In addition, ADIOS2 allows for setting a
user-defined number of data substreams/subfiles. See examples 5, 9, 12, 16.
- The integration order used in the ComputeLpError and ComputeElementLpError
methods of class GridFunction has been increased.
- Various other simplifications, extensions, and bugfixes in the code.
Version 4.1, released on March 10, 2020
=======================================
+1 -10
View File
@@ -396,12 +396,6 @@ MFEM_USE_SIDRE = YES/NO
blueprint specification. When enabled, this option requires installation of
HDF5 (see also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
MFEM_USE_SIMD = YES/NO
Enables the high performance templated classes to use architecture dependent
SIMD intrinsics instead of the generic implementation of class AutoSIMD in
linalg/simd/auto.hpp. This option should be combined with suitable
compiler options, such as -march=native, to enable optimal vectorization.
MFEM_USE_CONDUIT = YES/NO
Enables support for converting MFEM Mesh and Grid Function objects to and
from Conduit Mesh Blueprint Descriptions (https://github.com/LLNL/conduit/)
@@ -432,8 +426,6 @@ MFEM_USE_PUMI = YES/NO
data management system that is capable of handling general non-manifold
models and effectively supports automated adaptive analysis. PUMI enables
support for parallel unstructured mesh modifications in MFEM.
The develop branch of PUMI repository (https://github.com/SCOREC/core)
should be used for most updated features.
MFEM_USE_UMPIRE = YES/NO
Enables support for Umpire, a resource management library that allows the
@@ -617,9 +609,8 @@ The specific libraries and their options are:
- PUMI (optional), used when MFEM_USE_PUMI = YES.
URL: https://scorec.rpi.edu/pumi
https://github.com/SCOREC/core
Options: PUMI_OPT, PUMI_LIB.
Versions: PUMI >= 2.2.3.
Versions: PUMI >= 2.2.0.
- HiOp (optional), used when MFEM_USE_HIOP = YES.
URL: https://github.com/LLNL/hiop
-1
View File
@@ -47,7 +47,6 @@ set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
-3
View File
@@ -107,9 +107,6 @@
// Enable MFEM functionality based on the Sidre library
#cmakedefine MFEM_USE_SIDRE
// Enable the use of SIMD in the high performance templated classes
#cmakedefine MFEM_USE_SIMD
// Enable MFEM functionality based on Conduit
#cmakedefine MFEM_USE_CONDUIT
@@ -733,7 +733,7 @@ function(mfem_export_mk_files)
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GNUTLS
MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_MPFR MFEM_USE_SIDRE
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA
MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2)
MFEM_USE_UMPIRE)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
@@ -743,7 +743,6 @@ function(mfem_export_mk_files)
endforeach()
# TODO: Add support for MFEM_USE_CUDA=YES
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${MFEM_CXX})
set(MFEM_CPPFLAGS "")
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
MFEM_CXXFLAGS)
-3
View File
@@ -106,9 +106,6 @@
// Enable Sidre support
// #define MFEM_USE_SIDRE
// Enable the use of SIMD in the high performance templated classes
// #define MFEM_USE_SIMD
// Enable Conduit support
// #define MFEM_USE_CONDUIT
-2
View File
@@ -49,12 +49,10 @@ MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
MFEM_USE_CEED = @MFEM_USE_CEED@
MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
MFEM_USE_SIMD = @MFEM_USE_SIMD@
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
MFEM_HOST_CXX = @MFEM_HOST_CXX@
MFEM_CPPFLAGS = @MFEM_CPPFLAGS@
MFEM_CXXFLAGS = @MFEM_CXXFLAGS@
MFEM_TPLFLAGS = @MFEM_TPLFLAGS@
-1
View File
@@ -49,7 +49,6 @@ option(MFEM_USE_OCCA "Enable OCCA" OFF)
option(MFEM_USE_RAJA "Enable RAJA" OFF)
option(MFEM_USE_CEED "Enable CEED" OFF)
option(MFEM_USE_UMPIRE "Enable Umpire" OFF)
option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" ON)
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
-1
View File
@@ -137,7 +137,6 @@ MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_CEED = NO
MFEM_USE_UMPIRE = NO
MFEM_USE_SIMD = YES
MFEM_USE_ADIOS2 = NO
# Compile and link options for zlib.
+6 -13
View File
@@ -29,20 +29,8 @@
#define MFEM_ALWAYS_INLINE
#endif
// --- MFEM_VECTORIZE_LOOP (disabled)
#if (__cplusplus >= 201103L) && !defined(MFEM_DEBUG) && defined(__GNUC__)
//#define MFEM_VECTORIZE_LOOP _Pragma("GCC ivdep")
#define MFEM_VECTORIZE_LOOP
#else
#define MFEM_VECTORIZE_LOOP
#endif
// MFEM_TEMPLATE_BLOCK_SIZE is the block size used by the template matrix-matrix
// multiply, Mult_AB, defined in tmatrix.hpp. This parameter will generally
// require tuning to determine good value. It is probably highly influenced by
// the SIMD width when Mult_AB is used with a SIMD type like AutoSIMD.
#define MFEM_TEMPLATE_BLOCK_SIZE 4
#define MFEM_SIMD_SIZE 32
#define MFEM_TEMPLATE_ENABLE_SERIALIZE
// #define MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
@@ -50,6 +38,11 @@
// #define MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS
#define MFEM_TEMPLATE_INTRULE_COEFF_PRECOMP
// derived macros
#define MFEM_ROUNDUP(val,base) ((((val)+(base)-1)/(base))*(base))
#define MFEM_ALIGN_SIZE(size,type) \
MFEM_ROUNDUP(size,(MFEM_SIMD_SIZE)/sizeof(type))
#ifdef MFEM_COUNT_FLOPS
namespace mfem
{
+2
View File
@@ -64,6 +64,8 @@ if (MFEM_USE_MPI)
ex25p.cpp
ex26p.cpp
ex27p.cpp
pa_oper.cpp
io_benchmark.cpp
)
endif()
-34
View File
@@ -35,38 +35,6 @@
using namespace std;
using namespace mfem;
class CustomSolverMonitor : public IterativeSolverMonitor
{
public:
CustomSolverMonitor(const ParMesh *m,
ParGridFunction *f) :
pmesh(m),
pgf(f) {}
void MonitorSolution(int i, double norm, const Vector &x, bool final)
{
char vishost[] = "localhost";
int visport = 19916;
int num_procs, myid;
MPI_Comm_size(pmesh->GetComm(),&num_procs);
MPI_Comm_rank(pmesh->GetComm(),&myid);
pgf->SetFromTrueDofs(x);
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << *pgf
<< "window_title 'Iteration no " << i << "'"
<< "keys rRjlc\n" << flush;
}
private:
const ParMesh *pmesh;
ParGridFunction *pgf;
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
@@ -220,7 +188,6 @@ int main(int argc, char *argv[])
}
else
{
CustomSolverMonitor monitor(pmesh, &x);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetAbsTol(0.0);
gmres.SetRelTol(1e-12);
@@ -229,7 +196,6 @@ int main(int argc, char *argv[])
gmres.SetPrintLevel(1);
gmres.SetOperator(*A);
gmres.SetPreconditioner(*amg);
gmres.SetMonitor(monitor);
gmres.Mult(*B, *X);
}
delete amg;
+3 -3
View File
@@ -418,7 +418,7 @@ void FaceIntegrator::AssembleFaceVector(const FiniteElement &el1,
{
intorder++;
}
const IntegrationRule *ir = &IntRules.Get(Tr.GetGeometryType(), intorder);
const IntegrationRule *ir = &IntRules.Get(Tr.FaceGeom, intorder);
for (int i = 0; i < ir->GetNPoints(); i++)
{
@@ -435,10 +435,10 @@ void FaceIntegrator::AssembleFaceVector(const FiniteElement &el1,
elfun1_mat.MultTranspose(shape1, funval1);
elfun2_mat.MultTranspose(shape2, funval2);
Tr.SetIntPoint(&ip);
Tr.Face->SetIntPoint(&ip);
// Get the normal vector and the flux on the face
CalcOrtho(Tr.Jacobian(), nor);
CalcOrtho(Tr.Face->Jacobian(), nor);
const double mcs = rsolver.Eval(funval1, funval2, nor, fluxN);
// Update max char speed
+3 -44
View File
@@ -38,42 +38,6 @@
using namespace std;
using namespace mfem;
class GeneralResidualMonitor : public IterativeSolverMonitor
{
public:
GeneralResidualMonitor(const std::string& prefix_, int print_lvl)
: prefix(prefix_)
{
print_level = print_lvl;
}
virtual void MonitorResidual(int it, double norm, const Vector &r, bool final);
private:
const std::string prefix;
int print_level;
mutable double norm0;
};
void GeneralResidualMonitor::MonitorResidual(int it, double norm,
const Vector &r, bool final)
{
if (print_level == 1 || (print_level == 3 && (final || it == 0)))
{
mfem::out << prefix << " iteration " << setw(2) << it
<< " : ||r|| = " << norm;
if (it > 0)
{
mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
}
else
{
norm0 = norm;
}
mfem::out << '\n';
}
}
// Custom block preconditioner for the Jacobian of the incompressible nonlinear
// elasticity operator. It has the form
//
@@ -139,11 +103,9 @@ protected:
// Newton solver for the hyperelastic operator
NewtonSolver newton_solver;
GeneralResidualMonitor newton_monitor;
// Solver for the Jacobian solve in the Newton method
Solver *j_solver;
GeneralResidualMonitor j_monitor;
// Preconditioner for the Jacobian
Solver *j_prec;
@@ -448,8 +410,7 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
int iter,
Coefficient &c_mu)
: Operator(fes[0]->GetVSize() + fes[1]->GetVSize()),
newton_solver(), newton_monitor("Newton", 1),
j_monitor(" GMRES", 3), mu(c_mu), block_offsets(offsets)
newton_solver(), mu(c_mu), block_offsets(offsets)
{
Array<Vector *> rhs(2);
rhs = NULL; // Set all entries in the array
@@ -485,8 +446,7 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
j_gmres->SetRelTol(1e-12);
j_gmres->SetAbsTol(1e-12);
j_gmres->SetMaxIter(300);
j_gmres->SetPrintLevel(-1);
j_gmres->SetMonitor(j_monitor);
j_gmres->SetPrintLevel(0);
j_gmres->SetPreconditioner(*j_prec);
j_solver = j_gmres;
@@ -494,8 +454,7 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
newton_solver.iterative_mode = true;
newton_solver.SetSolver(*j_solver);
newton_solver.SetOperator(*this);
newton_solver.SetPrintLevel(-1);
newton_solver.SetMonitor(newton_monitor);
newton_solver.SetPrintLevel(1);
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(abs_tol);
newton_solver.SetMaxIter(iter);
+3 -60
View File
@@ -38,56 +38,6 @@
using namespace std;
using namespace mfem;
class GeneralResidualMonitor : public IterativeSolverMonitor
{
public:
GeneralResidualMonitor(MPI_Comm comm, const std::string& prefix_,
int print_lvl)
: prefix(prefix_)
{
#ifndef MFEM_USE_MPI
print_level = print_lvl;
#else
int rank;
MPI_Comm_rank(comm, &rank);
if (rank == 0)
{
print_level = print_lvl;
}
else
{
print_level = -1;
}
#endif
}
virtual void MonitorResidual(int it, double norm, const Vector &r, bool final);
private:
const std::string prefix;
int print_level;
mutable double norm0;
};
void GeneralResidualMonitor::MonitorResidual(int it, double norm,
const Vector &r, bool final)
{
if (print_level == 1 || (print_level == 3 && (final || it == 0)))
{
mfem::out << prefix << " iteration " << setw(2) << it
<< " : ||r|| = " << norm;
if (it > 0)
{
mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
}
else
{
norm0 = norm;
}
mfem::out << '\n';
}
}
// Custom block preconditioner for the Jacobian of the incompressible nonlinear
// elasticity operator. It has the form
//
@@ -153,11 +103,9 @@ protected:
// Newton solver for the hyperelastic operator
NewtonSolver newton_solver;
GeneralResidualMonitor newton_monitor;
// Solver for the Jacobian solve in the Newton method
Solver *j_solver;
GeneralResidualMonitor j_monitor;
// Preconditioner for the Jacobian
Solver *j_prec;
@@ -511,10 +459,7 @@ RubberOperator::RubberOperator(Array<ParFiniteElementSpace *> &fes,
int iter,
Coefficient &c_mu)
: Operator(fes[0]->TrueVSize() + fes[1]->TrueVSize()),
newton_solver(fes[0]->GetComm()),
newton_monitor(fes[0]->GetComm(), "Newton", 1),
j_monitor(fes[0]->GetComm(), " GMRES", 3),
mu(c_mu), block_trueOffsets(trueOffsets)
newton_solver(fes[0]->GetComm()), mu(c_mu), block_trueOffsets(trueOffsets)
{
Array<Vector *> rhs(2);
rhs = NULL; // Set all entries in the array
@@ -554,8 +499,7 @@ RubberOperator::RubberOperator(Array<ParFiniteElementSpace *> &fes,
j_gmres->SetRelTol(1e-12);
j_gmres->SetAbsTol(1e-12);
j_gmres->SetMaxIter(300);
j_gmres->SetPrintLevel(-1);
j_gmres->SetMonitor(j_monitor);
j_gmres->SetPrintLevel(0);
j_gmres->SetPreconditioner(*j_prec);
j_solver = j_gmres;
@@ -563,8 +507,7 @@ RubberOperator::RubberOperator(Array<ParFiniteElementSpace *> &fes,
newton_solver.iterative_mode = true;
newton_solver.SetSolver(*j_solver);
newton_solver.SetOperator(*this);
newton_solver.SetPrintLevel(-1);
newton_solver.SetMonitor(newton_monitor);
newton_solver.SetPrintLevel(1);
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(abs_tol);
newton_solver.SetMaxIter(iter);
+41 -7
View File
@@ -70,6 +70,7 @@ int main(int argc, char *argv[])
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
int nfiles = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -86,6 +87,7 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&nfiles, "-nf", "--num-files", "Number of files to write.");
args.Parse();
if (!args.Good())
{
@@ -158,7 +160,7 @@ int main(int argc, char *argv[])
{
fec = new H1_FECollection(order = 1, dim);
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec, 1, 0);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
@@ -237,20 +239,52 @@ int main(int argc, char *argv[])
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
std::string filename("nranks_");
filename += to_string(num_procs);
filename += ".gf";
{
double t1;
t1 = MPI_Wtime();
x.Save(filename.c_str(), nfiles);
double t2 = MPI_Wtime();
if (myid == 0)
{
err << "elapsed write time: " << t2 - t1 << endl;
}
}
{
double t1;
t1 = MPI_Wtime();
ParGridFunction new_x(fespace, filename.c_str());
double t2 = MPI_Wtime();
if (myid == 0)
{
err << "elapsed read time: " << t2 - t1 << endl;
}
// new_x -= x;
// out << "GF difference: " << new_x.Norml1() << endl;
}
// 15. 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);
//mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << num_procs << setfill('0') << setw(6) << myid;
//ofstream mesh_ofs(mesh_name.str().c_str());
//mesh_ofs.precision(8);
//pmesh->Print(mesh_ofs);
double t1 = MPI_Wtime();
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
double t2 = MPI_Wtime();
if (myid == 0)
{
err << t2 - t1 << endl;
}
}
// 16. Send the solution by socket to a GLVis server.
+77 -164
View File
@@ -6,7 +6,6 @@
// ex24 -m ../data/square-disc.mesh -o 2
// ex24 -m ../data/beam-tet.mesh
// ex24 -m ../data/beam-hex.mesh -o 2 -pa
// ex24 -m ../data/beam-hex.mesh -o 2 -pa -p 1
// ex24 -m ../data/escher.mesh
// ex24 -m ../data/escher.mesh -o 2
// ex24 -m ../data/fichera.mesh
@@ -24,15 +23,11 @@
// ex24 -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code illustrates usage of mixed finite element
// spaces, with two variants:
// spaces. Using two different approaches, we project a gradient
// of a function in H^1 to H(curl). Other spaces and example
// computations are to be added in the future.
//
// 1) (grad p, u) for p in H^1 tested against u in H(curl)
// 2) (div v, q) for v in H(div) tested against q in L_2
//
// Using different approaches, we project the gradient or
// divergence to the appropriate space.
//
// We recommend viewing examples 1, 3, and 5 before viewing this
// We recommend viewing examples 1 and 3 before viewing this
// example.
#include "mfem.hpp"
@@ -44,7 +39,6 @@ using namespace mfem;
double p_exact(const Vector &x);
void gradp_exact(const Vector &, Vector &);
double div_gradp_exact(const Vector &x);
int dim;
@@ -53,7 +47,6 @@ int main(int argc, char *argv[])
// 1. Parse command-line options.
const char *mesh_file = "../data/beam-hex.mesh";
int order = 1;
int prob = 0;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
@@ -64,8 +57,6 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&prob, "-p", "--problem-type",
"Choose between 0: H(Curl) or 1: H(Div)");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
@@ -109,107 +100,72 @@ int main(int argc, char *argv[])
}
mesh->ReorientTetMesh();
// 5. Define a finite element space on the mesh. Here we use Nedelec or
// Raviart-Thomas finite elements of the specified order.
FiniteElementCollection *trial_fec = NULL;
FiniteElementCollection *test_fec = NULL;
// 5. 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);
FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
FiniteElementSpace *H1fespace = new FiniteElementSpace(mesh, H1fec);
if (prob == 0)
{
trial_fec = new H1_FECollection(order, dim);
test_fec = new ND_FECollection(order, dim);
}
else
{
trial_fec = new RT_FECollection(order - 1, dim);
test_fec = new L2_FECollection(order - 1, dim);
}
int size = fespace->GetTrueVSize();
int H1size = H1fespace->GetTrueVSize();
cout << "Number of Nedelec finite element unknowns: " << size << endl;
cout << "Number of H1 finite element unknowns: " << H1size << endl;
FiniteElementSpace trial_fes(mesh, trial_fec);
FiniteElementSpace test_fes(mesh, test_fec);
int trial_size = trial_fes.GetTrueVSize();
int test_size = test_fes.GetTrueVSize();
if (prob == 0)
{
cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
cout << "Number of H1 finite element unknowns: " << trial_size << endl;
}
else
{
cout << "Number of Raviart-Thomas finite element unknowns: "
<< trial_size << endl;
cout << "Number of L2 finite element unknowns: " << test_size << endl;
}
// 6. Define the solution vector as a finite element grid function
// corresponding to the trial fespace.
GridFunction gftest(&test_fes);
GridFunction gftrial(&trial_fes);
GridFunction x(&test_fes);
// 6. 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.
GridFunction x(fespace);
FunctionCoefficient p_coef(p_exact);
GridFunction p(H1fespace);
p.ProjectCoefficient(p_coef);
p.SetTrueVector();
p.SetFromTrueVector();
VectorFunctionCoefficient gradp_coef(sdim, gradp_exact);
FunctionCoefficient divgradp_coef(div_gradp_exact);
if (prob == 0)
{
gftrial.ProjectCoefficient(p_coef);
}
else
{
gftrial.ProjectCoefficient(gradp_coef);
}
gftrial.SetTrueVector();
gftrial.SetFromTrueVector();
// 7. Set up the bilinear forms for L2 projection.
ConstantCoefficient one(1.0);
BilinearForm a(&test_fes);
MixedBilinearForm a_mixed(&trial_fes, &test_fes);
// 7. Set up the bilinear forms.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *sigma = new ConstantCoefficient(1.0);
BilinearForm *a = new BilinearForm(fespace);
MixedBilinearForm *a_NDH1 = new MixedBilinearForm(H1fespace, fespace);
if (pa)
{
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a_mixed.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a->SetAssemblyLevel(AssemblyLevel::PARTIAL);
a_NDH1->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
if (prob == 0)
{
a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
a_mixed.AddDomainIntegrator(new MixedVectorGradientIntegrator(one));
}
else
{
a.AddDomainIntegrator(new MassIntegrator(one));
a_mixed.AddDomainIntegrator(new VectorFEDivergenceIntegrator(one));
}
// First approach: L2 projection
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
a_NDH1->AddDomainIntegrator(new MixedVectorGradientIntegrator(*muinv));
// 8. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, etc.
if (static_cond) { a.EnableStaticCondensation(); }
// 8. 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();
if (!pa) { a.Finalize(); }
a->Assemble();
if (!pa) { a->Finalize(); }
a_mixed.Assemble();
if (!pa) { a_mixed.Finalize(); }
a_NDH1->Assemble();
if (!pa) { a_NDH1->Finalize(); }
if (pa)
{
a_mixed.Mult(gftrial, x);
a_NDH1->Mult(p, x);
}
else
{
SparseMatrix& mixed = a_mixed.SpMat();
mixed.Mult(gftrial, x);
SparseMatrix& NDH1 = a_NDH1->SpMat();
NDH1.Mult(p, x);
}
// 9. Define and apply a PCG solver for Ax = b with Jacobi preconditioner.
{
GridFunction rhs(&test_fes);
GridFunction rhs(fespace);
rhs = x;
x = 0.0;
@@ -220,15 +176,15 @@ int main(int argc, char *argv[])
if (pa)
{
Array<int> ess_tdof_list; // empty
OperatorJacobiSmoother Jacobi(a, ess_tdof_list);
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
cg.SetOperator(a);
cg.SetOperator(*a);
cg.SetPreconditioner(Jacobi);
cg.Mult(rhs, x);
}
else
{
SparseMatrix& Amat = a.SpMat();
SparseMatrix& Amat = a->SpMat();
DSmoother Jacobi(Amat);
cg.SetOperator(Amat);
@@ -237,68 +193,33 @@ int main(int argc, char *argv[])
}
}
// 10. Compute the same field by applying a DiscreteInterpolator.
GridFunction discreteInterpolant(&test_fes);
DiscreteLinearOperator dlo(&trial_fes, &test_fes);
if (prob == 0)
{
dlo.AddDomainInterpolator(new GradientInterpolator());
}
else
{
dlo.AddDomainInterpolator(new DivergenceInterpolator());
}
// 10. Second approach: compute the same solution by applying
// GradientInterpolator in H(curl).
DiscreteLinearOperator grad(H1fespace, fespace);
grad.AddDomainInterpolator(new GradientInterpolator());
grad.Assemble();
dlo.Assemble();
dlo.Mult(gftrial, discreteInterpolant);
GridFunction gradp(fespace);
grad.Mult(p, gradp);
// 11. Compute the projection of the exact field.
GridFunction exact_proj(&test_fes);
if (prob == 0)
{
exact_proj.ProjectCoefficient(gradp_coef);
}
else
{
exact_proj.ProjectCoefficient(divgradp_coef);
}
// 11. Compute the projection of the exact grad p.
GridFunction exact_gradp(fespace);
exact_gradp.ProjectCoefficient(gradp_coef);
exact_gradp.SetTrueVector();
exact_gradp.SetFromTrueVector();
exact_proj.SetTrueVector();
exact_proj.SetFromTrueVector();
// 12. Compute and print the L_2 norm of the error.
if (prob == 0)
// 12. Compute and print the L^2 norm of the error.
{
double errSol = x.ComputeL2Error(gradp_coef);
double errInterp = discreteInterpolant.ComputeL2Error(gradp_coef);
double errProj = exact_proj.ComputeL2Error(gradp_coef);
double errInterp = gradp.ComputeL2Error(gradp_coef);
double errProj = exact_gradp.ComputeL2Error(gradp_coef);
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
"|| E_h - grad p ||_{L^2} = " << errSol << '\n' << endl;
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - grad p"
"||_{L_2} = " << errInterp << '\n' << endl;
"||_{L^2} = " << errInterp << '\n' << endl;
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
"||_{L_2} = " << errProj << '\n' << endl;
}
else
{
int order_quad = max(2, 2*order+1);
const IntegrationRule *irs[Geometry::NumGeom];
for (int i=0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
double errSol = x.ComputeL2Error(divgradp_coef, irs);
double errInterp = discreteInterpolant.ComputeL2Error(divgradp_coef, irs);
double errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
cout << " Divergence interpolant f_h = div v_h in L_2: || f_h - div v"
"||_{L_2} = " << errInterp << '\n' << endl;
cout << " Projection f_h of exact div v in L_2: || f_h - div v "
"||_{L_2} = " << errProj << '\n' << endl;
"||_{L^2} = " << errProj << '\n' << endl;
}
// 13. Save the refined mesh and the solution. This output can be viewed
@@ -321,8 +242,14 @@ int main(int argc, char *argv[])
}
// 15. Free the used memory.
delete trial_fec;
delete test_fec;
delete a;
delete a_NDH1;
delete sigma;
delete muinv;
delete fespace;
delete H1fespace;
delete fec;
delete H1fec;
delete mesh;
return 0;
@@ -357,17 +284,3 @@ void gradp_exact(const Vector &x, Vector &f)
if (x.Size() == 3) { f(2) = 0.0; }
}
}
double div_gradp_exact(const Vector &x)
{
if (dim == 3)
{
return -3.0 * sin(x(0)) * sin(x(1)) * sin(x(2));
}
else if (dim == 2)
{
return -2.0 * sin(x(0)) * sin(x(1));
}
return 0.0;
}
+81 -171
View File
@@ -6,7 +6,6 @@
// mpirun -np 4 ex24p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex24p -m ../data/beam-tet.mesh
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -pa
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -p 1 -pa
// mpirun -np 4 ex24p -m ../data/escher.mesh
// mpirun -np 4 ex24p -m ../data/escher.mesh -o 2
// mpirun -np 4 ex24p -m ../data/fichera.mesh
@@ -24,15 +23,11 @@
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code illustrates usage of mixed finite element
// spaces, with two variants:
// spaces. Using two different approaches, we project a gradient
// of a function in H^1 to H(curl). Other spaces and example
// computations are to be added in the future.
//
// 1) (grad p, u) for p in H^1 tested against u in H(curl)
// 2) (div v, q) for v in H(div) tested against q in L_2
//
// Using different approaches, we project the gradient or
// divergence to the appropriate space.
//
// We recommend viewing examples 1, 3, and 5 before viewing this
// We recommend viewing examples 1 and 3 before viewing this
// example.
#include "mfem.hpp"
@@ -44,7 +39,6 @@ using namespace mfem;
double p_exact(const Vector &x);
void gradp_exact(const Vector &, Vector &);
double div_gradp_exact(const Vector &x);
int dim;
@@ -59,7 +53,6 @@ int main(int argc, char *argv[])
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-hex.mesh";
int order = 1;
int prob = 0;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
@@ -70,8 +63,6 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&prob, "-p", "--problem-type",
"Choose between 0: H(Curl) or 1: H(Div)");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
@@ -138,115 +129,80 @@ int main(int argc, char *argv[])
pmesh->ReorientTetMesh();
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use Nedelec or Raviart-Thomas finite elements of the specified order.
FiniteElementCollection *trial_fec = NULL;
FiniteElementCollection *test_fec = NULL;
if (prob == 0)
{
trial_fec = new H1_FECollection(order, dim);
test_fec = new ND_FECollection(order, dim);
}
else
{
trial_fec = new RT_FECollection(order - 1, dim);
test_fec = new L2_FECollection(order - 1, dim);
}
ParFiniteElementSpace trial_fes(pmesh, trial_fec);
ParFiniteElementSpace test_fes(pmesh, test_fec);
HYPRE_Int trial_size = trial_fes.GlobalTrueVSize();
HYPRE_Int test_size = test_fes.GlobalTrueVSize();
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
ParFiniteElementSpace *H1fespace = new ParFiniteElementSpace(pmesh, H1fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
HYPRE_Int H1size = H1fespace->GlobalTrueVSize();
if (myid == 0)
{
if (prob == 0)
{
cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
cout << "Number of H1 finite element unknowns: " << trial_size << endl;
}
else
{
cout << "Number of Raviart-Thomas finite element unknowns: "
<< trial_size << endl;
cout << "Number of L2 finite element unknowns: " << test_size << endl;
}
cout << "Number of Nedelec finite element unknowns: " << size << endl;
cout << "Number of H1 finite element unknowns: " << H1size << endl;
}
// 8. Define the solution vector as a parallel finite element grid function
// corresponding to the trial fespace.
ParGridFunction gftest(&test_fes);
ParGridFunction gftrial(&trial_fes);
ParGridFunction x(&test_fes);
// 8. 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);
FunctionCoefficient p_coef(p_exact);
ParGridFunction p(H1fespace);
p.ProjectCoefficient(p_coef);
p.SetTrueVector();
p.SetFromTrueVector();
VectorFunctionCoefficient gradp_coef(sdim, gradp_exact);
FunctionCoefficient divgradp_coef(div_gradp_exact);
if (prob == 0)
{
gftrial.ProjectCoefficient(p_coef);
}
else
{
gftrial.ProjectCoefficient(gradp_coef);
}
gftrial.SetTrueVector();
gftrial.SetFromTrueVector();
// 9. Set up the parallel bilinear forms for L2 projection.
ConstantCoefficient one(1.0);
ParBilinearForm a(&test_fes);
ParMixedBilinearForm a_mixed(&trial_fes, &test_fes);
// 9. Set up the parallel bilinear forms.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *sigma = new ConstantCoefficient(1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
ParMixedBilinearForm *a_NDH1 = new ParMixedBilinearForm(H1fespace, fespace);
if (pa)
{
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a_mixed.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a->SetAssemblyLevel(AssemblyLevel::PARTIAL);
a_NDH1->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
if (prob == 0)
{
a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
a_mixed.AddDomainIntegrator(new MixedVectorGradientIntegrator(one));
}
else
{
a.AddDomainIntegrator(new MassIntegrator(one));
a_mixed.AddDomainIntegrator(new VectorFEDivergenceIntegrator(one));
}
// First approach: L2 projection
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
a_NDH1->AddDomainIntegrator(new MixedVectorGradientIntegrator(*muinv));
// 10. 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(); }
if (static_cond) { a->EnableStaticCondensation(); }
a.Assemble();
if (!pa) { a.Finalize(); }
a->Assemble();
if (!pa) { a->Finalize(); }
a_mixed.Assemble();
if (!pa) { a_mixed.Finalize(); }
a_NDH1->Assemble();
if (!pa) { a_NDH1->Finalize(); }
Vector B(test_fes.GetTrueVSize());
Vector X(test_fes.GetTrueVSize());
Vector B(fespace->GetTrueVSize());
Vector X(fespace->GetTrueVSize());
if (pa)
{
ParLinearForm b(&test_fes); // used as a vector
a_mixed.Mult(gftrial, b); // process-local multiplication
b.ParallelAssemble(B);
ParLinearForm *b = new ParLinearForm(fespace); // used as a vector
a_NDH1->Mult(p, *b); // process-local multiplication
b->ParallelAssemble(B);
delete b;
}
else
{
HypreParMatrix *mixed = a_mixed.ParallelAssemble();
HypreParMatrix *NDH1 = a_NDH1->ParallelAssemble();
Vector P(trial_fes.GetTrueVSize());
gftrial.GetTrueDofs(P);
Vector P(H1fespace->GetTrueVSize());
p.GetTrueDofs(P);
mixed->Mult(P,B);
NDH1->Mult(P,B);
delete mixed;
delete NDH1;
}
// 11. Define and apply a parallel PCG solver for AX=B with Jacobi
@@ -256,9 +212,9 @@ int main(int argc, char *argv[])
Array<int> ess_tdof_list; // empty
OperatorPtr A;
a.FormSystemMatrix(ess_tdof_list, A);
a->FormSystemMatrix(ess_tdof_list, A);
OperatorJacobiSmoother Jacobi(a, ess_tdof_list);
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
@@ -271,7 +227,7 @@ int main(int argc, char *argv[])
}
else
{
HypreParMatrix *Amat = a.ParallelAssemble();
HypreParMatrix *Amat = a->ParallelAssemble();
HypreDiagScale Jacobi(*Amat);
HyprePCG pcg(*Amat);
pcg.SetTol(1e-12);
@@ -286,73 +242,35 @@ int main(int argc, char *argv[])
x.SetFromTrueDofs(X);
// 12. Compute the same field by applying a DiscreteInterpolator.
ParGridFunction discreteInterpolant(&test_fes);
ParDiscreteLinearOperator dlo(&trial_fes, &test_fes);
if (prob == 0)
{
dlo.AddDomainInterpolator(new GradientInterpolator());
}
else
{
dlo.AddDomainInterpolator(new DivergenceInterpolator());
}
// 12. Second approach: compute the same solution by applying
// GradientInterpolator in H(curl).
ParDiscreteLinearOperator grad(H1fespace, fespace);
grad.AddDomainInterpolator(new GradientInterpolator());
grad.Assemble();
dlo.Assemble();
dlo.Mult(gftrial, discreteInterpolant);
ParGridFunction gradp(fespace);
grad.Mult(p, gradp);
// 13. Compute the projection of the exact field.
ParGridFunction exact_proj(&test_fes);
if (prob == 0)
{
exact_proj.ProjectCoefficient(gradp_coef);
}
else
{
exact_proj.ProjectCoefficient(divgradp_coef);
}
// 13. Compute the projection of the exact grad p.
ParGridFunction exact_gradp(fespace);
exact_gradp.ProjectCoefficient(gradp_coef);
exact_gradp.SetTrueVector();
exact_gradp.SetFromTrueVector();
exact_proj.SetTrueVector();
exact_proj.SetFromTrueVector();
// 14. Compute and print the L_2 norm of the error.
if (prob == 0)
// 14. Compute and print the L^2 norm of the error.
{
double errSol = x.ComputeL2Error(gradp_coef);
double errInterp = discreteInterpolant.ComputeL2Error(gradp_coef);
double errProj = exact_proj.ComputeL2Error(gradp_coef);
double errInterp = gradp.ComputeL2Error(gradp_coef);
double errProj = exact_gradp.ComputeL2Error(gradp_coef);
if (myid == 0)
{
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - grad p"
"||_{L_2} = " << errInterp << '\n' << endl;
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in "
"H(curl): || E_h - grad p ||_{L^2} = " << errSol << '\n' << endl;
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - "
"grad p ||_{L^2} = " << errInterp << '\n' << endl;
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
"||_{L_2} = " << errProj << '\n' << endl;
}
}
else
{
int order_quad = max(2, 2*order+1);
const IntegrationRule *irs[Geometry::NumGeom];
for (int i=0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
double errSol = x.ComputeL2Error(divgradp_coef, irs);
double errInterp = discreteInterpolant.ComputeL2Error(divgradp_coef, irs);
double errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
if (myid == 0)
{
cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
cout << " Divergence interpolant f_h = div v_h in L_2: || f_h - div v"
"||_{L_2} = " << errInterp << '\n' << endl;
cout << " Projection f_h of exact div v in L_2: || f_h - div v "
"||_{L_2} = " << errProj << '\n' << endl;
"||_{L^2} = " << errProj << '\n' << endl;
}
}
@@ -384,8 +302,14 @@ int main(int argc, char *argv[])
}
// 17. Free the used memory.
delete trial_fec;
delete test_fec;
delete a;
delete a_NDH1;
delete sigma;
delete muinv;
delete fespace;
delete H1fespace;
delete fec;
delete H1fec;
delete pmesh;
MPI_Finalize();
@@ -422,17 +346,3 @@ void gradp_exact(const Vector &x, Vector &f)
if (x.Size() == 3) { f(2) = 0.0; }
}
}
double div_gradp_exact(const Vector &x)
{
if (dim == 3)
{
return -3.0 * sin(x(0)) * sin(x(1)) * sin(x(2));
}
else if (dim == 2)
{
return -2.0 * sin(x(0)) * sin(x(1));
}
return 0.0;
}
+7
View File
@@ -274,6 +274,13 @@ int main(int argc, char *argv[])
pmesh->SetNodalFESpace(fespace);
}
{
x.Save("ex2p.gf", 1);
ParGridFunction new_x(fespace, "ex2p.gf");
new_x -= x;
out << "GF difference: " << new_x.Norml1() << endl;
}
// 16. Save in parallel the displaced mesh and the inverted solution (which
// gives the backward displacements to the original grid). This output
// can be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
+26 -60
View File
@@ -6,7 +6,6 @@
// ex4 -m ../data/star.mesh
// ex4 -m ../data/beam-tet.mesh
// ex4 -m ../data/beam-hex.mesh
// ex4 -m ../data/beam-hex.mesh -o 2 -pa
// ex4 -m ../data/escher.mesh
// ex4 -m ../data/fichera.mesh -o 2 -hb
// ex4 -m ../data/fichera-q2.vtk
@@ -21,12 +20,6 @@
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
// ex4 -m ../data/star-surf.mesh -o 1
//
// Device sample runs:
// ex4 -m ../data/star.mesh -pa -d cuda
// ex4 -m ../data/star.mesh -pa -d raja-cuda
// ex4 -m ../data/star.mesh -pa -d raja-omp
// ex4 -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code solves a simple 2D/3D H(div) diffusion
// problem corresponding to the second order definite equation
// -grad(alpha div F) + beta F = f with boundary condition F dot n
@@ -62,8 +55,6 @@ int main(int argc, char *argv[])
bool set_bc = true;
bool static_cond = false;
bool hybridization = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -79,10 +70,6 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
"--no-hybridization", "Enable hybridization.");
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.");
@@ -95,19 +82,14 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
kappa = freq * M_PI;
// 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. Read the mesh from the given mesh file. We can handle triangular,
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume, as well as
// periodic meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 4. Refine the mesh to increase the resolution. In this example we do
// 3. Refine the mesh 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 25,000
// elements.
@@ -120,14 +102,14 @@ int main(int argc, char *argv[])
}
}
// 5. Define a finite element space on the mesh. Here we use the
// 4. Define a finite element space on the mesh. Here we use the
// Raviart-Thomas finite elements of the specified order.
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
cout << "Number of finite element unknowns: "
<< fespace->GetTrueVSize() << endl;
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
// 5. Determine the list of true (i.e. 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.
@@ -139,7 +121,7 @@ int main(int argc, char *argv[])
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 7. Set up the linear form b(.) which corresponds to the right-hand side
// 6. Set up the 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.
@@ -148,7 +130,7 @@ int main(int argc, char *argv[])
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
// 8. Define the solution vector x as a finite element grid function
// 7. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary faces will be used
// when eliminating the non-homogeneous boundary condition to modify the
@@ -157,17 +139,16 @@ int main(int argc, char *argv[])
VectorFunctionCoefficient F(sdim, F_exact);
x.ProjectCoefficient(F);
// 9. Set up the bilinear form corresponding to the H(div) diffusion operator
// 8. Set up the bilinear form corresponding to the H(div) diffusion operator
// grad alpha div + beta I, by adding the div-div and the mass domain
// integrators.
Coefficient *alpha = new ConstantCoefficient(1.0);
Coefficient *beta = new ConstantCoefficient(1.0);
BilinearForm *a = new BilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
// 10. Assemble the bilinear form and the corresponding linear system,
// 9. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, hybridization, etc.
@@ -186,47 +167,32 @@ int main(int argc, char *argv[])
}
a->Assemble();
OperatorPtr A;
SparseMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
cout << "Size of linear system: " << A->Height() << endl;
cout << "Size of linear system: " << A.Height() << endl;
// 11. Solve the linear system A X = B.
if (!pa)
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 10000, 1e-20, 0.0);
// 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system A X = B with PCG.
GSSmoother M(A);
PCG(A, M, B, X, 1, 10000, 1e-20, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
// 10. If compiled with SuiteSparse support, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(B, X);
#endif
}
else // Jacobi preconditioning in partial assembly mode
{
if (UsesTensorBasis(*fespace))
{
OperatorJacobiSmoother M(*a, ess_tdof_list);
PCG(*A, M, B, X, 1, 10000, 1e-20, 0.0);
}
else
{
CG(*A, B, X, 1, 10000, 1e-20, 0.0);
}
}
// 12. Recover the solution as a finite element grid function.
// 11. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
// 13. Compute and print the L^2 norm of the error.
// 12. Compute and print the L^2 norm of the error.
cout << "\n|| F_h - F ||_{L^2} = " << x.ComputeL2Error(F) << '\n' << endl;
// 14. Save the refined mesh and the solution. This output can be viewed
// 13. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
{
ofstream mesh_ofs("refined.mesh");
@@ -237,7 +203,7 @@ int main(int argc, char *argv[])
x.Save(sol_ofs);
}
// 15. Send the solution by socket to a GLVis server.
// 14. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -247,7 +213,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *mesh << x << flush;
}
// 16. Free the used memory.
// 15. Free the used memory.
delete hfes;
delete hfec;
delete a;
@@ -269,7 +235,7 @@ void F_exact(const Vector &p, Vector &F)
double x = p(0);
double y = p(1);
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
// double z = (dim == 3) ? p(2) : 0.0;
F(0) = cos(kappa*x)*sin(kappa*y);
F(1) = cos(kappa*y)*sin(kappa*x);
@@ -286,7 +252,7 @@ void f_exact(const Vector &p, Vector &f)
double x = p(0);
double y = p(1);
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
// double z = (dim == 3) ? p(2) : 0.0;
double temp = 1 + 2*kappa*kappa;
+29 -50
View File
@@ -6,7 +6,6 @@
// mpirun -np 4 ex4p -m ../data/star.mesh
// mpirun -np 4 ex4p -m ../data/beam-tet.mesh
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
@@ -20,12 +19,6 @@
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
//
// Device sample runs:
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code solves a simple 2D/3D H(div) diffusion
// problem corresponding to the second order definite equation
// -grad(alpha div F) + beta F = f with boundary condition F dot n
@@ -67,8 +60,6 @@ int main(int argc, char *argv[])
bool set_bc = true;
bool static_cond = false;
bool hybridization = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -84,10 +75,6 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
"--no-hybridization", "Enable hybridization.");
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.");
@@ -107,19 +94,14 @@ int main(int argc, char *argv[])
}
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
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume, as well as periodic meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 5. Refine the serial mesh on all processors to increase the resolution. In
// 4. 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.
@@ -132,7 +114,7 @@ int main(int argc, char *argv[])
}
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// 5. 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
@@ -148,7 +130,7 @@ int main(int argc, char *argv[])
}
pmesh->ReorientTetMesh();
// 7. Define a parallel finite element space on the parallel mesh. Here we
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use the Raviart-Thomas finite elements of the specified order.
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
@@ -158,7 +140,7 @@ int main(int argc, char *argv[])
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// 7. 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.
@@ -170,7 +152,7 @@ int main(int argc, char *argv[])
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 9. Set up the parallel linear form b(.) which corresponds to the
// 8. 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.
@@ -179,7 +161,7 @@ int main(int argc, char *argv[])
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
// 10. Define the solution vector x as a parallel finite element grid function
// 9. 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 faces will be used
// when eliminating the non-homogeneous boundary condition to modify the
@@ -188,17 +170,16 @@ int main(int argc, char *argv[])
VectorFunctionCoefficient F(sdim, F_exact);
x.ProjectCoefficient(F);
// 11. Set up the parallel bilinear form corresponding to the H(div)
// 10. Set up the parallel bilinear form corresponding to the H(div)
// diffusion operator grad alpha div + beta I, by adding the div-div and
// the mass domain integrators.
Coefficient *alpha = new ConstantCoefficient(1.0);
Coefficient *beta = new ConstantCoefficient(1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
// 12. Assemble the parallel bilinear form and the corresponding linear
// 11. 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,
@@ -218,43 +199,41 @@ int main(int argc, char *argv[])
}
a->Assemble();
OperatorPtr A;
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (myid == 0 && !pa)
HYPRE_Int glob_size = A.GetGlobalNumRows();
if (myid == 0)
{
cout << "Size of linear system: "
<< A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
cout << "Size of linear system: " << glob_size << endl;
}
// 13. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
// 12. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
// the 3D ADS preconditioners from hypre. If using hybridization, the
// system is preconditioned with hypre's BoomerAMG. In the partial
// assembly case, use Jacobi preconditioning.
Solver *prec = NULL;
CGSolver *pcg = new CGSolver(MPI_COMM_WORLD);
pcg->SetOperator(*A);
// system is preconditioned with hypre's BoomerAMG.
HypreSolver *prec = NULL;
CGSolver *pcg = new CGSolver(A.GetComm());
pcg->SetOperator(A);
pcg->SetRelTol(1e-12);
pcg->SetMaxIter(2000);
pcg->SetMaxIter(500);
pcg->SetPrintLevel(1);
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
if (hybridization) { prec = new HypreBoomerAMG(A); }
else
{
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
if (dim == 2) { prec = new HypreAMS(*A.As<HypreParMatrix>(), prec_fespace); }
else { prec = new HypreADS(*A.As<HypreParMatrix>(), prec_fespace); }
if (dim == 2) { prec = new HypreAMS(A, prec_fespace); }
else { prec = new HypreADS(A, prec_fespace); }
}
pcg->SetPreconditioner(*prec);
pcg->Mult(B, X);
// 14. Recover the parallel grid function corresponding to X. This is the
// 13. 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.
// 14. Compute and print the L^2 norm of the error.
{
double err = x.ComputeL2Error(F);
if (myid == 0)
@@ -263,7 +242,7 @@ int main(int argc, char *argv[])
}
}
// 16. Save the refined mesh and the solution in parallel. This output can
// 15. 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;
@@ -279,7 +258,7 @@ int main(int argc, char *argv[])
x.Save(sol_ofs);
}
// 17. Send the solution by socket to a GLVis server.
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -290,7 +269,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 18. Free the used memory.
// 17. Free the used memory.
delete pcg;
delete prec;
delete hfes;
@@ -316,7 +295,7 @@ void F_exact(const Vector &p, Vector &F)
double x = p(0);
double y = p(1);
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
// double z = (dim == 3) ? p(2) : 0.0;
F(0) = cos(kappa*x)*sin(kappa*y);
F(1) = cos(kappa*y)*sin(kappa*x);
@@ -333,7 +312,7 @@ void f_exact(const Vector &p, Vector &f)
double x = p(0);
double y = p(1);
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
// double z = (dim == 3) ? p(2) : 0.0;
double temp = 1 + 2*kappa*kappa;
+24 -76
View File
@@ -4,10 +4,8 @@
//
// Sample runs: ex5 -m ../data/square-disc.mesh
// ex5 -m ../data/star.mesh
// ex5 -m ../data/star.mesh -pa
// ex5 -m ../data/beam-tet.mesh
// ex5 -m ../data/beam-hex.mesh
// ex5 -m ../data/beam-hex.mesh -pa
// ex5 -m ../data/escher.mesh
// ex5 -m ../data/fichera.mesh
//
@@ -49,7 +47,6 @@ int main(int argc, char *argv[])
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool pa = false;
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -57,8 +54,6 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -151,39 +146,22 @@ int main(int argc, char *argv[])
BilinearForm *mVarf(new BilinearForm(R_space));
MixedBilinearForm *bVarf(new MixedBilinearForm(R_space, W_space));
if (pa) { mVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
mVarf->AddDomainIntegrator(new VectorFEMassIntegrator(k));
mVarf->Assemble();
if (!pa) { mVarf->Finalize(); }
mVarf->Finalize();
SparseMatrix &M(mVarf->SpMat());
if (pa) { bVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
bVarf->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf->Assemble();
if (!pa) { bVarf->Finalize(); }
bVarf->Finalize();
SparseMatrix & B(bVarf->SpMat());
B *= -1.;
SparseMatrix *BT = Transpose(B);
BlockOperator darcyOp(block_offsets);
TransposeOperator *Bt = NULL;
if (pa)
{
Bt = new TransposeOperator(bVarf);
darcyOp.SetBlock(0,0, mVarf);
darcyOp.SetBlock(0,1, Bt, -1.0);
darcyOp.SetBlock(1,0, bVarf, -1.0);
}
else
{
SparseMatrix &M(mVarf->SpMat());
SparseMatrix &B(bVarf->SpMat());
B *= -1.;
Bt = new TransposeOperator(&B);
darcyOp.SetBlock(0,0, &M);
darcyOp.SetBlock(0,1, Bt);
darcyOp.SetBlock(1,0, &B);
}
BlockMatrix darcyMatrix(block_offsets);
darcyMatrix.SetBlock(0,0, &M);
darcyMatrix.SetBlock(0,1, BT);
darcyMatrix.SetBlock(1,0, &B);
// 9. Construct the operators for preconditioner
//
@@ -192,57 +170,27 @@ int main(int argc, char *argv[])
//
// Here we use Symmetric Gauss-Seidel to approximate the inverse of the
// pressure Schur Complement
SparseMatrix *MinvBt = NULL;
Vector Md(mVarf->Height());
SparseMatrix *MinvBt = Transpose(B);
Vector Md(M.Height());
M.GetDiag(Md);
for (int i = 0; i < Md.Size(); i++)
{
MinvBt->ScaleRow(i, 1./Md(i));
}
SparseMatrix *S = Mult(B, *MinvBt);
BlockDiagonalPreconditioner darcyPrec(block_offsets);
Solver *invM, *invS;
SparseMatrix *S = NULL;
if (pa)
{
mVarf->AssembleDiagonal(Md);
Vector invMd(mVarf->Height());
for (int i=0; i<mVarf->Height(); ++i)
{
invMd(i) = 1.0 / Md(i);
}
Vector BMBt_diag(bVarf->Height());
bVarf->AssembleDiagonal_ADAt(invMd, BMBt_diag);
Array<int> ess_tdof_list; // empty
invM = new OperatorJacobiSmoother(Md, ess_tdof_list);
invS = new OperatorJacobiSmoother(BMBt_diag, ess_tdof_list);
}
else
{
SparseMatrix &M(mVarf->SpMat());
M.GetDiag(Md);
SparseMatrix &B(bVarf->SpMat());
MinvBt = Transpose(B);
for (int i = 0; i < Md.Size(); i++)
{
MinvBt->ScaleRow(i, 1./Md(i));
}
S = Mult(B, *MinvBt);
invM = new DSmoother(M);
invM = new DSmoother(M);
#ifndef MFEM_USE_SUITESPARSE
invS = new GSSmoother(*S);
invS = new GSSmoother(*S);
#else
invS = new UMFPackSolver(*S);
invS = new UMFPackSolver(*S);
#endif
}
invM->iterative_mode = false;
invS->iterative_mode = false;
BlockDiagonalPreconditioner darcyPrec(block_offsets);
darcyPrec.SetDiagonalBlock(0, invM);
darcyPrec.SetDiagonalBlock(1, invS);
@@ -258,7 +206,7 @@ int main(int argc, char *argv[])
solver.SetAbsTol(atol);
solver.SetRelTol(rtol);
solver.SetMaxIter(maxIter);
solver.SetOperator(darcyOp);
solver.SetOperator(darcyMatrix);
solver.SetPreconditioner(darcyPrec);
solver.SetPrintLevel(1);
x = 0.0;
@@ -347,8 +295,8 @@ int main(int argc, char *argv[])
delete invM;
delete invS;
delete S;
delete Bt;
delete MinvBt;
delete BT;
delete mVarf;
delete bVarf;
delete W_space;
+25 -84
View File
@@ -4,10 +4,8 @@
//
// Sample runs: mpirun -np 4 ex5p -m ../data/square-disc.mesh
// mpirun -np 4 ex5p -m ../data/star.mesh
// mpirun -np 4 ex5p -m ../data/star.mesh -r 2 -pa
// mpirun -np 4 ex5p -m ../data/beam-tet.mesh
// mpirun -np 4 ex5p -m ../data/beam-hex.mesh
// mpirun -np 4 ex5p -m ../data/beam-hex.mesh -pa
// mpirun -np 4 ex5p -m ../data/escher.mesh
// mpirun -np 4 ex5p -m ../data/fichera.mesh
//
@@ -56,25 +54,19 @@ int main(int argc, char *argv[])
// 2. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int ref_levels = -1;
int order = 1;
bool par_format = false;
bool pa = false;
bool visualization = 1;
bool adios2 = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&par_format, "-pf", "--parallel-format", "-sf",
"--serial-format",
"Format to use when saving the results for VisIt.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -105,13 +97,10 @@ int main(int argc, char *argv[])
// 4. 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 10,000 elements, unless the user specifies it as input.
// more than 10,000 elements.
{
if (ref_levels == -1)
{
ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
}
int ref_levels =
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
@@ -207,47 +196,25 @@ int main(int argc, char *argv[])
ParBilinearForm *mVarf(new ParBilinearForm(R_space));
ParMixedBilinearForm *bVarf(new ParMixedBilinearForm(R_space, W_space));
HypreParMatrix *M = NULL;
HypreParMatrix *B = NULL;
HypreParMatrix *M, *B;
if (pa) { mVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
mVarf->AddDomainIntegrator(new VectorFEMassIntegrator(k));
mVarf->Assemble();
if (!pa) { mVarf->Finalize(); }
mVarf->Finalize();
M = mVarf->ParallelAssemble();
if (pa) { bVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
bVarf->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf->Assemble();
if (!pa) { bVarf->Finalize(); }
bVarf->Finalize();
B = bVarf->ParallelAssemble();
(*B) *= -1;
HypreParMatrix *BT = B->Transpose();
BlockOperator *darcyOp = new BlockOperator(block_trueOffsets);
Array<int> empty_tdof_list; // empty
OperatorPtr opM, opB;
TransposeOperator *Bt = NULL;
if (pa)
{
mVarf->FormSystemMatrix(empty_tdof_list, opM);
bVarf->FormRectangularSystemMatrix(empty_tdof_list, empty_tdof_list, opB);
Bt = new TransposeOperator(opB.Ptr());
darcyOp->SetBlock(0,0, opM.Ptr());
darcyOp->SetBlock(0,1, Bt, -1.0);
darcyOp->SetBlock(1,0, opB.Ptr(), -1.0);
}
else
{
M = mVarf->ParallelAssemble();
B = bVarf->ParallelAssemble();
(*B) *= -1;
Bt = new TransposeOperator(B);
darcyOp->SetBlock(0,0, M);
darcyOp->SetBlock(0,1, Bt);
darcyOp->SetBlock(1,0, B);
}
darcyOp->SetBlock(0,0, M);
darcyOp->SetBlock(0,1, BT);
darcyOp->SetBlock(1,0, B);
// 11. Construct the operators for preconditioner
//
@@ -256,43 +223,17 @@ int main(int argc, char *argv[])
//
// Here we use Symmetric Gauss-Seidel to approximate the inverse of the
// pressure Schur Complement.
HypreParMatrix *MinvBt = NULL;
HypreParVector *Md = NULL;
HypreParMatrix *S = NULL;
Vector Md_PA;
Solver *invM, *invS;
HypreParMatrix *MinvBt = B->Transpose();
HypreParVector *Md = new HypreParVector(MPI_COMM_WORLD, M->GetGlobalNumRows(),
M->GetRowStarts());
M->GetDiag(*Md);
if (pa)
{
Md_PA.SetSize(R_space->GetTrueVSize());
mVarf->AssembleDiagonal(Md_PA);
Vector invMd(Md_PA.Size());
for (int i=0; i<Md_PA.Size(); ++i)
{
invMd(i) = 1.0 / Md_PA(i);
}
MinvBt->InvScaleRows(*Md);
HypreParMatrix *S = ParMult(B, MinvBt);
Vector BMBt_diag(W_space->GetTrueVSize());
bVarf->AssembleDiagonal_ADAt(invMd, BMBt_diag);
Array<int> ess_tdof_list; // empty
invM = new OperatorJacobiSmoother(Md_PA, ess_tdof_list);
invS = new OperatorJacobiSmoother(BMBt_diag, ess_tdof_list);
}
else
{
Md = new HypreParVector(MPI_COMM_WORLD, M->GetGlobalNumRows(),
M->GetRowStarts());
M->GetDiag(*Md);
MinvBt = B->Transpose();
MinvBt->InvScaleRows(*Md);
S = ParMult(B, MinvBt);
invM = new HypreDiagScale(*M);
invS = new HypreBoomerAMG(*S);
}
HypreSolver *invM, *invS;
invM = new HypreDiagScale(*M);
invS = new HypreBoomerAMG(*S);
invM->iterative_mode = false;
invS->iterative_mode = false;
@@ -304,7 +245,7 @@ int main(int argc, char *argv[])
// 12. Solve the linear system with MINRES.
// Check the norm of the unpreconditioned residual.
int maxIter(pa ? 1000 : 500);
int maxIter(500);
double rtol(1.e-6);
double atol(1.e-10);
@@ -454,7 +395,7 @@ int main(int argc, char *argv[])
delete S;
delete Md;
delete MinvBt;
delete Bt;
delete BT;
delete B;
delete M;
delete mVarf;
+1 -11
View File
@@ -19,7 +19,6 @@
//
// Device sample runs:
// ex9 -pa
// ex9 -ea
// ex9 -pa -m ../data/periodic-cube.mesh
// ex9 -pa -m ../data/periodic-cube.mesh -d cuda
//
@@ -143,7 +142,6 @@ int main(int argc, char *argv[])
int ref_levels = 2;
int order = 3;
bool pa = false;
bool ea = false;
const char *device_config = "cpu";
int ode_solver_type = 4;
double t_final = 10.0;
@@ -168,8 +166,6 @@ int main(int argc, char *argv[])
"Order (degree) of the finite elements.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
@@ -273,11 +269,6 @@ int main(int argc, char *argv[])
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
else if (ea)
{
m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
m.AddDomainIntegrator(new MassIntegrator);
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k.AddInteriorFaceIntegrator(
@@ -438,9 +429,8 @@ FE_Evolution::FE_Evolution(BilinearForm &_M, BilinearForm &_K, const Vector &_b)
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b), z(_M.Height())
{
bool pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
bool ea = M.GetAssemblyLevel() == AssemblyLevel::ELEMENT;
Array<int> ess_tdof_list;
if (pa || ea)
if (pa)
{
M_prec = new OperatorJacobiSmoother(M, ess_tdof_list);
M_solver.SetOperator(M);
+2 -12
View File
@@ -19,7 +19,6 @@
//
// Device sample runs:
// mpirun -np 4 ex9p -pa
// mpirun -np 4 ex9p -ea
// mpirun -np 4 ex9p -pa -m ../data/periodic-cube.mesh
// mpirun -np 4 ex9p -pa -m ../data/periodic-cube.mesh -d cuda
//
@@ -162,7 +161,6 @@ int main(int argc, char *argv[])
int par_ref_levels = 0;
int order = 3;
bool pa = false;
bool ea = false;
const char *device_config = "cpu";
int ode_solver_type = 4;
double t_final = 10.0;
@@ -190,8 +188,6 @@ int main(int argc, char *argv[])
"Order (degree) of the finite elements.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
@@ -323,11 +319,6 @@ int main(int argc, char *argv[])
m->SetAssemblyLevel(AssemblyLevel::PARTIAL);
k->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
else if (ea)
{
m->SetAssemblyLevel(AssemblyLevel::ELEMENT);
k->SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
m->AddDomainIntegrator(new MassIntegrator);
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k->AddInteriorFaceIntegrator(
@@ -565,9 +556,8 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
z(_M.Height())
{
bool pa = _M.GetAssemblyLevel()==AssemblyLevel::PARTIAL;
bool ea = _M.GetAssemblyLevel()==AssemblyLevel::ELEMENT;
if (pa || ea)
if (pa)
{
M.Reset(&_M, false);
K.Reset(&_K, false);
@@ -581,7 +571,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
M_solver.SetOperator(*M);
Array<int> ess_tdof_list;
if (pa || ea)
if (pa)
{
M_prec = new OperatorJacobiSmoother(_M, ess_tdof_list);
dg_solver = NULL;
+292
View File
@@ -0,0 +1,292 @@
// MFEM Example 1 - Parallel Version
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
// mpirun -np 4 ex1p -m ../data/star.mesh
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
// mpirun -np 4 ex1p -m ../data/escher.mesh
// mpirun -np 4 ex1p -m ../data/fichera.mesh
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// mpirun -np 4 ex1p -pa -d cuda
// mpirun -np 4 ex1p -pa -d occa-cuda
// mpirun -np 4 ex1p -pa -d raja-omp
// mpirun -np 4 ex1p -pa -d ceed-cpu
// mpirun -np 4 ex1p -pa -d ceed-cuda
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of essential boundary conditions, static condensation, and the
// optional connection to the GLVis tool for visualization.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "mpi.h"
using namespace std;
using namespace mfem;
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/star.mesh";
const char *mesh_file = "../data/square-disc.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = false;
int nfiles = 1;
// const char *out_file = "0_0.gf";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
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.AddOption(&nfiles, "-nf", "--num-files", "Number of files to write.");
// args.AddOption(&out_file, "-o", "--outfile",
// "Name of file to write.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 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);
int dim = mesh->Dimension();
// 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 10,000 elements.
{
int ref_levels =
(int)floor(log(10000./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.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
}
else if (pmesh->GetNodes())
{
fec = pmesh->GetNodes()->OwnFEC();
if (myid == 0)
{
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
}
else
{
fec = new H1_FECollection(order = 1, dim);
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec, 1, 0);
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;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 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
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm *b = new ParLinearForm(fespace);
ConstantCoefficient one(1.0);
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(fespace);
x = 0.0;
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DiffusionIntegrator(one));
// 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);
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * With partial assembly, use Jacobi smoothing, for now.
Solver *prec = NULL;
if (pa)
{
if (UsesTensorBasis(*fespace))
{
prec = new OperatorJacobiSmoother(*a, ess_tdof_list);
}
}
else
{
prec = new HypreBoomerAMG;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec) { cg.SetPreconditioner(*prec); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
std::string filename = to_string(num_procs) + "_" + to_string(nfiles) + "_";
{
double t1;
t1 = MPI_Wtime();
x.Save(filename.c_str(), nfiles);
double t2 = MPI_Wtime();
double write_time = t2 - t1;
double average_write_time;
MPI_Reduce(&write_time, &average_write_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
{
std::cout << "Average write time: " << average_write_time / num_procs << " for "
<< nfiles << " files and " << num_procs << " ranks\n";
}
}
{
double t1;
t1 = MPI_Wtime();
ParGridFunction temp_gf(fespace, filename.c_str());
double t2 = MPI_Wtime();
double read_time = t2 - t1;
double average_read_time;
MPI_Reduce(&read_time, &average_read_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
{
std::cout << "Average read time: " << average_read_time / num_procs << " for "
<< nfiles << " files and " << num_procs << " ranks\n";
}
}
// 17. Free the used memory.
delete a;
delete b;
delete fespace;
if (order > 0) { delete fec; }
delete pmesh;
MPI_Finalize();
return 0;
}
+906
View File
@@ -0,0 +1,906 @@
// MFEM Example 9
//
// Compile with: make serial_nogpu
//
// Description: This code solves the time-dependent advection-diffusion
// equation:
// \frac(\partial u}{\partial t}
// = \mathbf{a} \cdot \Nabla u - \nu \Nabla^2 u
// where a is a given advection velocity, \nu is the diffusion
// parameter, and u0(x) = u(0,x) is a given initial condition.
//
// The demonstrates explicit time marching with H1 elements of
// arbitrary order. Periodic boundary conditions are used through
// periodic meshes. GLVis can be used for visualization of a
// time-evolving solution.
#include <fstream>
#include <iostream>
#include <algorithm>
#include "mfem.hpp"
#include "mpi.h"
using namespace std;
using namespace mfem;
/** A time-dependent operator for the right-hand side of the ODE. The weak
form of du/dt = -a.grad(u) + nu Delta(u) is M du/dt = K u + b, where M and
K are the mass and advection-diffusion matrices, and b describes the flow
on the boundary. This can be written as a general ODE,
du/dt = M^{-1} (K u + b), and this class is used to evaluate the right-hand
side. */
class AdvectionDiffusionEvolution : public mfem::TimeDependentOperator
{
public:
/// \param[in] M - bilinear form for mass matrix
/// \param[in] K - bilinear form for stiffness matrix
/// \param[in] b - load vector
AdvectionDiffusionEvolution(mfem::BilinearForm &M, mfem::BilinearForm &K,
const mfem::Vector &b);
/// Perform the action of the operator: y = k = f(x, t), where k solves
/// Compute k = M^-1(Kx + l)
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
/// Solve the implicit equation: k = f(x + dt k, t), for the unknown k at
/// the current time t.
void ImplicitSolve(const double dt, const mfem::Vector &x,
mfem::Vector &k) override;
virtual ~AdvectionDiffusionEvolution();
private:
mfem::BilinearForm &M, &K;
const mfem::Vector &b;
/// solver for inverting mass matrix for explicit time-marching
std::unique_ptr<mfem::Solver> M_prec;
mfem::CGSolver M_solver;
/// solver for implicit time-marching
mfem::GSSmoother prec;
mfem::GMRESSolver linear_solver;
mfem::NewtonSolver newton;
mutable mfem::Vector z;
/// pointer-to-implementation idiom
/// Hides implementation details of this operator
class SystemOperator;
/// Operator that combines the linear spatial discretization with
/// the load vector into one operator used for implicit solves
std::unique_ptr<SystemOperator> combined_oper;
/// sets the state and dt for the combined operator
/// \param[in] dt - time increment
/// \param[in] x - the current state
void setOperParameters(double dt, const mfem::Vector *x);
};
class PAJacobianOperator : public mfem::Operator
{
public:
PAJacobianOperator(mfem::ParBilinearForm &_mass,
mfem::ParBilinearForm &_stiff);
/// Compute r = J@k = M@k + dt*K@k
/// \param[in] k - dx/dt
/// \param[out] r - J@k = M@k + dt*K@k
void Mult(const mfem::Vector &k, mfem::Vector &r) const override;
/// Set current dt values - needed to compute action of Jacobian.
void setParameters(double dt);
private:
mfem::ParBilinearForm &mass;
mfem::ParBilinearForm &stiff;
double dt;
};
class ParSystemOperator : public mfem::Operator
{
public:
/// Nonlinear operator of the form that combines the mass, res, stiff,
/// and load elements for implicit/explicit ODE integration
/// \param[in] ess_bdr - array of boundaries attributes marked essential
/// \param[in] mass - bilinear form for mass matrix (not owned)
/// \param[in] res - nonlinear residual operator (not owned)
/// \param[in] stiff - bilinear form for stiffness matrix (not owned)
/// \param[in] load - load vector (not owned)
/// \param[in] a - used to move the spatial residual to the rhs
ParSystemOperator(mfem::ParBilinearForm &_mass,
mfem::ParBilinearForm &_stiff);
/// Compute r = M@k + K@(x+dt*k)
/// (with `@` denoting matrix-vector multiplication)
/// \param[in] k - dx/dt
/// \param[out] r - the residual
/// \note the signs on each operator must be accounted for elsewhere
void Mult(const mfem::Vector &k, mfem::Vector &r) const override;
/// Compute J = M + dt * K
/// \param[in] k - dx/dt
mfem::Operator &GetGradient(const mfem::Vector &k) const override;
/// Set current dt and x values - needed to compute action and Jacobian.
void setParameters(double _dt, const mfem::Vector *_x);
~ParSystemOperator();
private:
mfem::ParBilinearForm &mass;
mfem::ParBilinearForm &stiff;
mutable mfem::HypreParMatrix *jacobian, *stiff_jacobian;
double dt;
const mfem::Vector *x;
mutable mfem::Vector work, work2;
std::unique_ptr<PAJacobianOperator> pa_jac;
};
/** A time-dependent operator for the right-hand side of the ODE. The weak
form of du/dt = -a.grad(u) + nu Delta(u) is M du/dt = K u + b, where M and
K are the mass and advection-diffusion matrices, and b describes the flow
on the boundary. This can be written as a general ODE,
du/dt = M^{-1} (K u + b), and this class is used to evaluate the right-hand
side. */
class ParAdvectionDiffusionEvolution : public mfem::TimeDependentOperator
{
public:
/// \param[in] M - parallel bilinear form for mass matrix
/// \param[in] K - parallel bilinear form for stiffness matrix
ParAdvectionDiffusionEvolution(mfem::ParBilinearForm &M,
mfem::ParBilinearForm &K);
/// Perform the action of the operator: y = k = f(x, t), where k solves
/// Compute k = M^-1(Kx + l)
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
/// Solve the implicit equation: k = f(x + dt k, t), for the unknown k at
/// the current time t.
void ImplicitSolve(const double dt, const mfem::Vector &x,
mfem::Vector &k) override;
virtual ~ParAdvectionDiffusionEvolution();
private:
mfem::OperatorHandle M_;
mfem::ParBilinearForm &M, &K;
/// solver for inverting mass matrix for explicit time-marching
std::unique_ptr<mfem::Solver> M_prec;
mfem::CGSolver M_solver;
/// solver for implicit time-marching
mfem::Solver *prec;
mfem::GMRESSolver linear_solver;
mfem::NewtonSolver newton;
mfem::Vector diag;
mutable mfem::Vector z, work, work2;
/// pointer-to-implementation idiom
/// Hides implementation details of this operator
/// Operator that combines the linear spatial discretization with
/// the load vector into one operator used for implicit solves
std::unique_ptr<ParSystemOperator> combined_oper;
/// sets the state and dt for the combined operator
/// \param[in] dt - time increment
/// \param[in] x - the current state
void setOperParameters(double dt, const mfem::Vector *x);
};
// Choice for the problem setup. The fluid velocity, initial condition and
// inflow boundary condition are chosen based on this parameter.
int problem;
// Velocity coefficient
void velocity_function(const Vector &X, Vector &v);
// Initial condition
double u0_function(const Vector &X);
// Inflow boundary condition
double inflow_function(const Vector &X, const double t);
// Mesh bounding box
Vector bb_min, bb_max;
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.
problem = 3;
const char *mesh_file = "../data/periodic-square.mesh";
int ser_ref_levels = 0;
int par_ref_levels = 0;
int order = 3;
const char *device_config = "cpu";
int ode_solver_type = 22;
double t_final = 3 * 2*M_PI;
double dt = 0.01;
bool glvis = false;
bool paraview = false;
int vis_steps = 5;
double nu_val = 0.001;
int precision = 8;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use. See options in velocity_function().");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&glvis, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.AddOption(&nu_val, "-nu", "--nu-value",
"Value for \nu, the parameter that controls diffusion.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
std::cout << "Num ranks: " << num_procs << "\n";
args.PrintOptions(cout);
}
Device device(device_config);
if (myid == 0) { device.Print(); }
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter. If the mesh is of NURBS type, we convert it
// to a (piecewise-polynomial) high-order mesh.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 6. Define the 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.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 7. Define the finite element space of the given
// polynomial order on the refined mesh.
H1_FECollection fec(order, dim, BasisType::GaussLobatto);
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of unknowns: " << global_vSize << endl;
}
// 8. Set up and assemble the bilinear and linear forms corresponding to the
// CG discretization.
/// negative to move the diffusion terms to the right side
ConstantCoefficient nu(-nu_val);
ConstantCoefficient one(1.0);
VectorFunctionCoefficient velocity(dim, velocity_function);
FunctionCoefficient u0(u0_function);
ParBilinearForm *m_pa = new ParBilinearForm(fes);
ParBilinearForm *k_pa = new ParBilinearForm(fes);
m_pa->SetAssemblyLevel(AssemblyLevel::PARTIAL);
k_pa->SetAssemblyLevel(AssemblyLevel::PARTIAL);
/// create mass matrix
m_pa->AddDomainIntegrator(new MassIntegrator(one));
/// add advection terms to stiffness matrix
k_pa->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
/// add diffusion terms to stiffness matrix
k_pa->AddDomainIntegrator(new DiffusionIntegrator(nu));
m_pa->Assemble();
int skip_zeros = 0;
k_pa->Assemble(skip_zeros);
m_pa->Finalize();
k_pa->Finalize(skip_zeros);
ParBilinearForm *m = new ParBilinearForm(fes);
ParBilinearForm *k = new ParBilinearForm(fes);
/// create mass matrix
m->AddDomainIntegrator(new MassIntegrator);
/// add advection terms to stiffness matrix
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
/// add diffusion terms to stiffness matrix
k->AddDomainIntegrator(new DiffusionIntegrator(nu));
m->Assemble();
k->Assemble(skip_zeros);
m->Finalize();
k->Finalize(skip_zeros);
ParGridFunction *u = new ParGridFunction(fes);
u->UseDevice(true);
u->ProjectCoefficient(u0);
HypreParVector *U = u->GetTrueDofs();
ParSystemOperator pso(*m, *k);
ParSystemOperator pso_pa(*m_pa, *k_pa);
pso.setParameters(dt, U);
pso_pa.setParameters(dt, U);
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_r(U->Size());
double t1 = MPI_Wtime();
pso.Mult(*U, pso_r);
double t2 = MPI_Wtime();
double fa_mult_time = t2 - t1;
double average_fa_mult_time;
MPI_Reduce(&fa_mult_time, &average_fa_mult_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "FA Mult time: " << average_fa_mult_time / num_procs << endl;
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_pa_r(U->Size());
double t3 = MPI_Wtime();
pso_pa.Mult(*U, pso_pa_r);
double t4 = MPI_Wtime();
double pa_mult_time = t4 - t3;
double average_pa_mult_time;
MPI_Reduce(&pa_mult_time, &average_pa_mult_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "FA Mult time: " << average_pa_mult_time / num_procs << endl;
double local_mult_speedup = (t2-t1) / (t4-t3);
double global_mult_speedup;
MPI_Reduce(&local_mult_speedup, &global_mult_speedup, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "PA mult speedup: " << global_mult_speedup / num_procs << endl;
mfem::Vector diff_r(pso_pa_r);
diff_r -= pso_r;
// std::cout << "r diff: " << diff_r.Norml2() << std::endl;
mfem::Operator &pso_jac = pso.GetGradient(*U);
mfem::Operator &pso_pa_jac = pso_pa.GetGradient(*U);
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_jac_r(U->Size());
double t5 = MPI_Wtime();
pso_jac.Mult(*U, pso_jac_r);
double t6 = MPI_Wtime();
double fa_jac_mult_time = t6-t5;
double average_fa_jac_time;
MPI_Reduce(&fa_jac_mult_time, &average_fa_jac_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "FA Jac Mult time: " << average_fa_jac_time / num_procs << endl;
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_pa_jac_r(U->Size());
double t7 = MPI_Wtime();
pso_pa_jac.Mult(*U, pso_pa_jac_r);
double t8 = MPI_Wtime();
double pa_jac_mult_time = t8-t7;
double average_pa_jac_time;
MPI_Reduce(&pa_jac_mult_time, &average_pa_jac_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "PA Jac Mult time: " << average_pa_jac_time / num_procs << endl;
double local_jac_speedup = (t6-t5) / (t8-t7);
double global_jac_speedup;
MPI_Reduce(&local_jac_speedup, &global_jac_speedup, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "PA Jac mult speedup: " << global_jac_speedup / num_procs << endl;
// 13. Free the used memory.
delete U;
delete u;
delete k;
delete m;
delete fes;
delete pmesh;
MPI_Finalize();
return 0;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 3:
{
// Translations in 1D, 2D, and 3D
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
break;
}
break;
}
case 1:
case 2:
{
// Clockwise rotation in 2D around the origin
const double w = M_PI/2;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
}
break;
}
case 0:
{
// Clockwise twisting rotation in 2D around the origin
const double w = M_PI/2;
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
d = d*d;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
}
break;
}
}
}
// Initial condition
double u0_function(const Vector &x)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
case 1:
{
switch (dim)
{
case 1:
return exp(-40.*pow(X(0)-0.5,2));
case 2:
case 3:
{
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
if (dim == 3)
{
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
rx *= s;
ry *= s;
}
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
}
}
}
case 2:
{
double x_ = X(0), y_ = X(1), rho, phi;
rho = hypot(x_, y_);
phi = atan2(y_, x_);
return pow(sin(M_PI*rho),2)*sin(3*phi);
}
case 3:
{
const double f = M_PI;
return sin(f*X(0))*sin(f*X(1));
}
}
return 0.0;
}
// Inflow boundary condition (zero for the problems considered in this example)
double inflow_function(const Vector &x, const double t)
{
switch (problem)
{
case 0:
case 1:
case 2:
case 3: return 0.0;
}
return 0.0;
}
class AdvectionDiffusionEvolution::SystemOperator : public mfem::Operator
{
public:
/// Nonlinear operator of the form that combines the mass, res, stiff,
/// and load elements for implicit/explicit ODE integration
/// \param[in] mass - bilinear form for mass matrix (not owned)
/// \param[in] res - nonlinear residual operator (not owned)
/// \param[in] stiff - bilinear form for stiffness matrix (not owned)
/// \param[in] load - load vector (not owned)
/// \param[in] a - used to move the spatial residual to the rhs
SystemOperator(BilinearForm &_mass, BilinearForm &_stiff,
const mfem::Vector &b)
: Operator(_mass.Height()), mass(_mass), stiff(_stiff),
load(b), Jacobian(NULL), dt(0.0), x(NULL), work(height)
{ }
/// Compute r = M@k + K@(x+dt*k) + l
/// (with `@` denoting matrix-vector multiplication)
/// \param[in] k - dx/dt
/// \param[out] r - the residual
/// \note the signs on each operator must be accounted for elsewhere
void Mult(const mfem::Vector &k, mfem::Vector &r) const override
{
/// work = x+dt*k = x+dt*dx/dt = x+dx
add(1.0, *x, dt, k, work);
r = 0.0;
stiff.AddMult(work, r);
r += load;
mass.AddMult(k, r, -1.0);
}
/// Compute J = M + dt * K
/// \param[in] k - dx/dt
mfem::Operator &GetGradient(const mfem::Vector &k) const override
{
delete Jacobian;
Jacobian = Add(-1.0, mass.SpMat(), dt, stiff.SpMat());
return *Jacobian;
}
/// Set current dt and x values - needed to compute action and Jacobian.
void setParameters(double _dt, const mfem::Vector *_x)
{
dt = _dt;
x = _x;
};
~SystemOperator() {delete Jacobian;};
private:
BilinearForm &mass;
BilinearForm &stiff;
const mfem::Vector &load;
mutable mfem::SparseMatrix *Jacobian;
double dt;
const mfem::Vector *x;
mutable mfem::Vector work, work2;
};
AdvectionDiffusionEvolution::AdvectionDiffusionEvolution(
BilinearForm &_M, BilinearForm &_K, const Vector &_b)
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b),
z(_M.Height())
{
bool pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
Array<int> ess_tdof_list;
if (pa)
{
M_prec.reset(new OperatorJacobiSmoother(M, ess_tdof_list));
M_solver.SetOperator(M);
}
else
{
M_prec.reset(new DSmoother(M.SpMat()));
M_solver.SetOperator(M.SpMat());
}
combined_oper.reset(new SystemOperator(_M, _K, _b));
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
linear_solver.iterative_mode = true;
linear_solver.SetRelTol(1e-12);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(100);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(prec);
newton.iterative_mode = false;
newton.SetRelTol(1e-9);
newton.SetAbsTol(0.0);
newton.SetMaxIter(100);
newton.SetPrintLevel(-1);
newton.SetSolver(linear_solver);
newton.SetOperator(*combined_oper);
}
void AdvectionDiffusionEvolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K.Mult(x, z);
z += b;
M_solver.Mult(z, y);
}
void AdvectionDiffusionEvolution::ImplicitSolve(const double dt,
const Vector &x,
Vector &k)
{
setOperParameters(dt, &x);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton.Mult(zero, k);
MFEM_VERIFY(newton.GetConverged(), "Newton solver did not converge!");
}
void AdvectionDiffusionEvolution::setOperParameters(double dt,
const mfem::Vector *x)
{
combined_oper->setParameters(dt, x);
}
AdvectionDiffusionEvolution::~AdvectionDiffusionEvolution() {}
PAJacobianOperator::PAJacobianOperator(ParBilinearForm &_mass, ParBilinearForm &_stiff)
: Operator(_mass.ParFESpace()->GetTrueVSize()), mass(_mass), stiff(_stiff),
dt(0.0) { }
void PAJacobianOperator::Mult(const mfem::Vector &k, mfem::Vector &r) const
{
r.UseDevice(true);
r = 0.0;
stiff.TrueAddMult(k, r, dt);
mass.TrueAddMult(k, r, -1.0);
}
void PAJacobianOperator::setParameters(const double _dt)
{
dt = _dt;
};
ParSystemOperator::ParSystemOperator(ParBilinearForm &_mass, ParBilinearForm &_stiff)
: Operator(_mass.ParFESpace()->GetTrueVSize()), mass(_mass), stiff(_stiff),
jacobian(NULL), stiff_jacobian(NULL), dt(0.0), x(NULL),
work(height)
{
pa_jac.reset(new PAJacobianOperator(mass, stiff));
}
/// Compute r = M@k + K@(x+dt*k)
/// (with `@` denoting matrix-vector multiplication)
/// \param[in] k - dx/dt
/// \param[out] r - the residual
/// \note the signs on each operator must be accounted for elsewhere
void ParSystemOperator::Mult(const mfem::Vector &k, mfem::Vector &r) const
{
r = 0.0;
work.UseDevice(true);
work = 0.0;
/// work = x+dt*k = x+dt*dx/dt = x+dx
if (x)
{
add(1.0, *x, dt, k, work);
}
stiff.TrueAddMult(work, r);
mass.TrueAddMult(k, r, -1.0);
}
/// Compute J = M + dt * K
/// \param[in] k - dx/dt
mfem::Operator &ParSystemOperator::GetGradient(const mfem::Vector &k) const
{
bool mass_pa = mass.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
bool stiff_pa = stiff.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
if (mass_pa && stiff_pa)
{
return *pa_jac.get();
}
else
{
delete stiff_jacobian;
delete jacobian;
jacobian = mass.ParallelAssemble();
*jacobian *= -1.0; //alpha;
stiff_jacobian = stiff.ParallelAssemble();
jacobian->Add(dt, *stiff_jacobian);
return *jacobian;
}
}
/// Set current dt and x values - needed to compute action and Jacobian.
void ParSystemOperator::setParameters(const double _dt, const mfem::Vector *_x)
{
dt = _dt;
x = _x;
pa_jac->setParameters(_dt);
};
ParSystemOperator::~ParSystemOperator()
{
delete jacobian;
delete stiff_jacobian;
};
ParAdvectionDiffusionEvolution::ParAdvectionDiffusionEvolution(
ParBilinearForm &_M, ParBilinearForm &_K)
: TimeDependentOperator(_M.ParFESpace()->GetTrueVSize()), M(_M), K(_K), z(_M.Height())
{
bool mass_pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
bool stiff_pa = K.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
Array<int> ess_tdof_list;
M_solver = CGSolver(MPI_COMM_WORLD);
if (mass_pa)
{
M_prec.reset(new OperatorJacobiSmoother(M, ess_tdof_list));
M_solver.SetOperator(M);
}
else
{
M_.Reset(_M.ParallelAssemble(), true);
// M_prec.reset(new HypreSmoother());
// M_solver.SetOperator(M.As<HypreParMatrix>());
HypreParMatrix &M_mat = *M_.As<HypreParMatrix>();
// HypreParMatrix &K_mat = *K.As<HypreParMatrix>();
M_prec.reset(new HypreSmoother(M_mat, HypreSmoother::Jacobi));
}
combined_oper.reset(new ParSystemOperator(_M, _K));
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
if (mass_pa && stiff_pa)
{
diag.UseDevice(true);
diag.SetSize(M.ParFESpace()->GetTrueVSize());
diag = 0.0;
work.UseDevice(true);
work2.UseDevice(true);
work.SetSize(M.ParFESpace()->GetTrueVSize());
work2.SetSize(M.ParFESpace()->GetTrueVSize());
work = 0.0;
work2 = 0.0;
M.AssembleDiagonal(work);
ParBilinearForm k(M.ParFESpace());
ConstantCoefficient nu(-0.01);
k.AddDomainIntegrator(new mfem::DiffusionIntegrator(nu));
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.Assemble(0);
k.Finalize(0);
k.AssembleDiagonal(work2);
double dt = 0.1;
add(-1.0, work, dt, work2, diag);
prec = new OperatorChebyshevSmoother(combined_oper.get(), diag,
ess_tdof_list, 5,
M.ParFESpace()->GetComm());
}
else
{
prec = new HypreSmoother();
}
linear_solver = GMRESSolver(MPI_COMM_WORLD);
linear_solver.iterative_mode = true;
linear_solver.SetRelTol(1e-12);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(2000);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(*prec);
linear_solver.SetKDim(2000);
newton.iterative_mode = true;
newton.SetRelTol(1e-9);
newton.SetAbsTol(0.0);
newton.SetMaxIter(10);
newton.SetPrintLevel(-1);
newton.SetSolver(linear_solver);
newton.SetOperator(*combined_oper);
}
void ParAdvectionDiffusionEvolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K.Mult(x, z);
M_solver.Mult(z, y);
}
void ParAdvectionDiffusionEvolution::ImplicitSolve(const double dt,
const Vector &x,
Vector &k)
{
setOperParameters(dt, &x);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton.Mult(zero, k);
MFEM_VERIFY(newton.GetConverged(), "Newton solver did not converge!");
}
void ParAdvectionDiffusionEvolution::setOperParameters(const double dt,
const mfem::Vector *x)
{
combined_oper->setParameters(dt, x);
}
ParAdvectionDiffusionEvolution::~ParAdvectionDiffusionEvolution() {delete prec;}
-7
View File
@@ -32,13 +32,6 @@
// is used for the Finite Element order and "-go" is used for the
// geometry order. Note that they can be used independently, i.e.
// "-o 8 -go 3" solves for 8th order FE on a third order geometry.
//
// NOTE: Model/Mesh files for this example are in the (large) data file
// repository of MFEM here https://github.com/mfem/data under the
// folder named "pumi", which consists of the following sub-folders:
// a) geom --> model files
// b) parallel --> parallel pumi mesh files
// c) serial --> serial pumi mesh files
#include "mfem.hpp"
#include <fstream>
-8
View File
@@ -36,14 +36,6 @@
// option "-o" is used for the Finite Element order and "-go" for
// the geometry order. Note that they can be used independently:
// "-o 8 -go 3" solves for 8th order FE on third order geometry.
//
// NOTE: Model/Mesh files for this example are in the (large) data file
// repository of MFEM here https://github.com/mfem/data under the
// folder named "pumi", which consists of the following sub-folders:
// a) geom --> model files
// b) parallel --> parallel pumi mesh files
// c) serial --> serial pumi mesh files
#include "mfem.hpp"
#include <fstream>
-8
View File
@@ -43,14 +43,6 @@
// also illustrated.
//
// We recommend viewing Example 1 before viewing this example.
//
// NOTE: Model/Mesh files for this example are in the (large) data file
// repository of MFEM here https://github.com/mfem/data under the
// folder named "pumi", which consists of the following sub-folders:
// a) geom --> model files
// b) parallel --> parallel pumi mesh files
// c) serial --> serial pumi mesh files
#include "mfem.hpp"
#include <fstream>
+2 -8
View File
@@ -1,7 +1,7 @@
// MFEM Example 6 - Parallel Version
// PUMI Modification
//
// Compile with: make ex6p
// Compile with: make ex1p
//
// Sample runs: mpirun -np 8 ex6p
//
@@ -18,13 +18,6 @@
// is added to modify the "adapt_ratio" which is the fraction of
// allowable error that scales the output size field of the error
// estimator.
//
// NOTE: Model/Mesh files for this example are in the (large) data file
// repository of MFEM here https://github.com/mfem/data under the
// folder named "pumi", which consists of the following sub-folders:
// a) geom --> model files
// b) parallel --> parallel pumi mesh files
// c) serial --> serial pumi mesh files
#include "mfem.hpp"
#include <fstream>
@@ -339,6 +332,7 @@ int main(int argc, char *argv[])
apf::destroyField(Tmag_field);
apf::destroyField(ipfield);
apf::destroyNumbering(pumi_mesh->findNumbering("LocalVertexNumbering"));
// 18. Perform MesAdapt.
ma::Input* erinput = ma::configure(pumi_mesh, sizefield);
+4 -11
View File
@@ -13,20 +13,13 @@ set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
bilininteg_convection_pa.cpp
bilininteg_convection_ea.cpp
bilininteg_dgtrace_pa.cpp
bilininteg_dgtrace_ea.cpp
bilininteg_diffusion_pa.cpp
bilininteg_diffusion_ea.cpp
bilininteg_convection.cpp
bilininteg_dgtrace.cpp
bilininteg_diffusion.cpp
bilininteg_divergence.cpp
bilininteg_hcurl.cpp
bilininteg_hdiv.cpp
bilininteg_vectorfe.cpp
bilininteg_gradient.cpp
bilininteg_mass_pa.cpp
bilininteg_mass_ea.cpp
bilininteg_transpose_ea.cpp
bilininteg_mass.cpp
bilininteg_vecdiffusion.cpp
bilininteg_vecmass.cpp
coefficient.cpp
+1 -3
View File
@@ -15,8 +15,6 @@
#include "adios2datacollection.hpp"
#ifdef MFEM_USE_ADIOS2
namespace mfem
{
@@ -89,4 +87,4 @@ noexcept
} //end namespace mfem
#endif // MFEM_USE_ADIOS2
-5
View File
@@ -17,9 +17,6 @@
#define MFEM_ADIOS2DATACOLLECTION
#include "../config/config.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../general/adios2stream.hpp"
#include "datacollection.hpp"
@@ -88,6 +85,4 @@ private:
} // namespace mfem
#endif // MFEM_USE_ADIOS2
#endif /* MFEM_ADIOS2DATACOLLECTION */
+2 -49
View File
@@ -126,7 +126,8 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
ext = new EABilinearFormExtension(this);
mfem_error("Element assembly not supported yet... stay tuned!");
// ext = new EABilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext = new PABilinearFormExtension(this);
@@ -1431,54 +1432,6 @@ void MixedBilinearForm::Assemble (int skip_zeros)
}
}
void MixedBilinearForm::AssembleDiagonal_ADAt(const Vector &D,
Vector &diag) const
{
if (ext)
{
MFEM_ASSERT(diag.Size() == test_fes->GetTrueVSize(),
"Vector for holding diagonal has wrong size!");
MFEM_ASSERT(D.Size() == trial_fes->GetTrueVSize(),
"Vector for holding diagonal has wrong size!");
const Operator *P_trial = trial_fes->GetProlongationMatrix();
const Operator *P_test = test_fes->GetProlongationMatrix();
if (!IsIdentityProlongation(P_trial))
{
Vector local_D(P_trial->Height());
P_trial->Mult(D, local_D);
if (!IsIdentityProlongation(P_test))
{
Vector local_diag(P_test->Height());
ext->AssembleDiagonal_ADAt(local_D, local_diag);
P_test->MultTranspose(local_diag, diag);
}
else
{
ext->AssembleDiagonal_ADAt(local_D, diag);
}
}
else
{
if (!IsIdentityProlongation(P_test))
{
Vector local_diag(P_test->Height());
ext->AssembleDiagonal_ADAt(D, local_diag);
P_test->MultTranspose(local_diag, diag);
}
else
{
ext->AssembleDiagonal_ADAt(D, diag);
}
}
}
else
{
MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
"matrix and use SparseMatrix functions?");
}
}
void MixedBilinearForm::ConformingAssemble()
{
if (assembly != AssemblyLevel::FULL)
-4
View File
@@ -705,10 +705,6 @@ public:
void Assemble(int skip_zeros = 1);
/** @brief Assemble the diagonal of ADA^T into diag, where A is this mixed
bilinear form and D is a diagonal. */
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
/// Get the input finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return trial_fes->GetProlongationMatrix(); }
+4 -376
View File
@@ -47,7 +47,7 @@ PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
bdr_face_restrict_lex = NULL;
}
void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
void PABilinearFormExtension::SetupRestrictionOperators()
{
ElementDofOrdering ordering = UsesTensorBasis(*a->FESpace())?
ElementDofOrdering::LEXICOGRAPHIC:
@@ -65,8 +65,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
if (int_face_restrict_lex == NULL && a->GetFBFI()->Size() > 0)
{
int_face_restrict_lex = trialFes->GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Interior);
ElementDofOrdering::LEXICOGRAPHIC, FaceType::Interior);
faceIntX.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
faceIntY.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
faceIntY.UseDevice(true); // ensure 'faceIntY = 0.0' is done on device
@@ -75,9 +74,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
if (bdr_face_restrict_lex == NULL && a->GetBFBFI()->Size() > 0)
{
bdr_face_restrict_lex = trialFes->GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Boundary,
m);
ElementDofOrdering::LEXICOGRAPHIC, FaceType::Boundary);
faceBdrX.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
faceBdrY.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
faceBdrY.UseDevice(true); // ensure 'faceBoundY = 0.0' is done on device
@@ -86,7 +83,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
void PABilinearFormExtension::Assemble()
{
SetupRestrictionOperators(L2FaceValues::DoubleValued);
SetupRestrictionOperators();
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
@@ -290,311 +287,6 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
}
}
// Data and methods for element-assembled bilinear forms
EABilinearFormExtension::EABilinearFormExtension(BilinearForm *form)
: PABilinearFormExtension(form)
{
}
void EABilinearFormExtension::Assemble()
{
SetupRestrictionOperators(L2FaceValues::SingleValued);
ne = trialFes->GetMesh()->GetNE();
elemDofs = trialFes->GetFE(0)->GetDof();
ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType());
ea_data.UseDevice(true);
ea_data = 0.0;
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssembleEA(*a->FESpace(), ea_data);
}
faceDofs = trialFes ->
GetTraceElement(0, trialFes->GetMesh()->GetFaceBaseGeometry(0)) ->
GetDof();
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int intFaceIntegratorCount = intFaceIntegrators.Size();
if (intFaceIntegratorCount>0)
{
nf_int = trialFes->GetNFbyType(FaceType::Interior);
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
ea_data_int = 0.0;
ea_data_ext = 0.0;
}
for (int i = 0; i < intFaceIntegratorCount; ++i)
{
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
ea_data_int,
ea_data_ext);
}
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
if (boundFaceIntegratorCount>0)
{
nf_bdr = trialFes->GetNFbyType(FaceType::Boundary);
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs, Device::GetMemoryType());
ea_data_bdr = 0.0;
}
for (int i = 0; i < boundFaceIntegratorCount; ++i)
{
bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr);
}
}
void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
// Apply the Element Restriction
const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
if (!useRestrict)
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
}
else
{
elem_restrict->Mult(x, localX);
localY = 0.0;
}
// Apply the Element Matrices
const int NDOFS = elemDofs;
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
MFEM_FORALL(glob_j, ne*NDOFS,
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(i, j, e)*X(i, e);
}
Y(j, e) += res;
});
// Apply the Element Restriction transposed
if (useRestrict)
{
elem_restrict->MultTranspose(localY, y);
}
// Treatment of interior faces
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
// Apply the Interior Face Restriction
int_face_restrict_lex->Mult(x, faceIntX);
if (faceIntX.Size()>0)
{
faceIntY = 0.0;
// Apply the interior face matrices
const int NDOFS = faceDofs;
auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int);
auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int);
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(i, j, 0, f)*X(i, 0, f);
}
Y(j, 0, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(i, j, 1, f)*X(i, 1, f);
}
Y(j, 1, f) += res;
});
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(i, j, 0, f)*X(i, 0, f);
}
Y(j, 1, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(i, j, 1, f)*X(i, 1, f);
}
Y(j, 0, f) += res;
});
// Apply the Interior Face Restriction transposed
int_face_restrict_lex->MultTranspose(faceIntY, y);
}
}
// Treatment of boundary faces
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (bdr_face_restrict_lex && bFISz>0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, faceBdrX);
if (faceBdrX.Size()>0)
{
faceBdrY = 0.0;
// Apply the boundary face matrices
const int NDOFS = faceDofs;
auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr);
auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
MFEM_FORALL(glob_j, nf_bdr*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
}
}
}
void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
// Apply the Element Restriction
const bool useRestrict = DeviceCanUseCeed() || !elem_restrict;
if (!useRestrict)
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
}
else
{
elem_restrict->Mult(x, localX);
localY = 0.0;
}
// Apply the Element Matrices transposed
const int NDOFS = elemDofs;
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
MFEM_FORALL(glob_j, ne*NDOFS,
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, e)*X(i, e);
}
Y(j, e) += res;
});
// Apply the Element Restriction transposed
if (useRestrict)
{
elem_restrict->MultTranspose(localY, y);
}
// Treatment of interior faces
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
// Apply the Interior Face Restriction
int_face_restrict_lex->Mult(x, faceIntX);
if (faceIntX.Size()>0)
{
faceIntY = 0.0;
// Apply the interior face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int);
auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int);
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(j, i, 0, f)*X(i, 0, f);
}
Y(j, 0, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(j, i, 1, f)*X(i, 1, f);
}
Y(j, 1, f) += res;
});
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(j, i, 0, f)*X(i, 0, f);
}
Y(j, 1, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(j, i, 1, f)*X(i, 1, f);
}
Y(j, 0, f) += res;
});
// Apply the Interior Face Restriction transposed
int_face_restrict_lex->MultTranspose(faceIntY, y);
}
}
// Treatment of boundary faces
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (bdr_face_restrict_lex && bFISz>0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, faceBdrX);
if (faceBdrX.Size()>0)
{
faceBdrY = 0.0;
// Apply the boundary face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr);
auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
MFEM_FORALL(glob_j, nf_bdr*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
double res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
}
}
}
MixedBilinearFormExtension::MixedBilinearFormExtension(MixedBilinearForm *form)
: Operator(form->Height(), form->Width()), a(form)
{
@@ -795,68 +487,4 @@ void PAMixedBilinearFormExtension::AddMultTranspose(const Vector &x, Vector &y,
}
}
void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
Vector &diag) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (elem_restrict_trial)
{
const ElementRestriction* H1elem_restrict_trial =
dynamic_cast<const ElementRestriction*>(elem_restrict_trial);
if (H1elem_restrict_trial)
{
H1elem_restrict_trial->MultUnsigned(D, localTrial);
}
else
{
elem_restrict_trial->Mult(D, localTrial);
}
}
if (elem_restrict_test)
{
localTest = 0.0;
for (int i = 0; i < iSz; ++i)
{
if (elem_restrict_trial)
{
integrators[i]->AssembleDiagonalPA_ADAt(localTrial, localTest);
}
else
{
integrators[i]->AssembleDiagonalPA_ADAt(D, localTest);
}
}
const ElementRestriction* H1elem_restrict_test =
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (H1elem_restrict_test)
{
H1elem_restrict_test->MultTransposeUnsigned(localTest, diag);
}
else
{
elem_restrict_test->MultTranspose(localTest, diag);
}
}
else
{
diag.UseDevice(true); // typically this is a large vector, so store on device
diag = 0.0;
for (int i = 0; i < iSz; ++i)
{
if (elem_restrict_trial)
{
integrators[i]->AssembleDiagonalPA_ADAt(localTrial, diag);
}
else
{
integrators[i]->AssembleDiagonalPA_ADAt(D, diag);
}
}
}
}
} // namespace mfem
+22 -26
View File
@@ -78,6 +78,26 @@ public:
~FABilinearFormExtension() {}
};
/// Data and methods for element-assembled bilinear forms
class EABilinearFormExtension : public BilinearFormExtension
{
public:
EABilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form) { }
/// TODO
void Assemble() {}
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
void Update() {}
~EABilinearFormExtension() {}
};
/// Data and methods for partially-assembled bilinear forms
class PABilinearFormExtension : public BilinearFormExtension
{
@@ -93,6 +113,7 @@ protected:
public:
PABilinearFormExtension(BilinearForm*);
void SetupRestrictionOperators();
void Assemble();
void AssembleDiagonal(Vector &diag) const;
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
@@ -100,32 +121,12 @@ public:
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
protected:
void SetupRestrictionOperators(const L2FaceValues m);
};
/// Data and methods for element-assembled bilinear forms
class EABilinearFormExtension : public PABilinearFormExtension
{
protected:
int ne;
int elemDofs;
Vector ea_data;
int nf_int, nf_bdr;
int faceDofs;
Vector ea_data_int, ea_data_ext, ea_data_bdr;
public:
EABilinearFormExtension(BilinearForm *form);
void Assemble();
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
};
/// Data and methods for matrix-free bilinear forms
class MFBilinearFormExtension : public BilinearFormExtension
@@ -185,8 +186,6 @@ public:
virtual void AddMultTranspose(const Vector &x, Vector &y,
const double c=1.0) const = 0;
virtual void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const = 0;
virtual void Update() = 0;
};
@@ -237,9 +236,6 @@ public:
void MultTranspose(const Vector &x, Vector &y) const;
/// y += c*A^T*x
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
/// Assemble the diagonal of ADA^T for a diagonal vector D.
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
/// Update internals for when a new MixedBilinearForm is given to this class
void Update();
};
+28 -50
View File
@@ -47,37 +47,7 @@ void BilinearFormIntegrator::AssemblePABoundaryFaces(const FiniteElementSpace&)
void BilinearFormIntegrator::AssembleDiagonalPA(Vector &)
{
mfem_error ("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &emat)
{
mfem_error ("BilinearFormIntegrator::AssembleEA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
&fes,
Vector &ea_data_int,
Vector &ea_data_ext)
{
mfem_error ("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
&fes,
Vector &ea_data_bdr)
{
mfem_error ("BilinearFormIntegrator::AssembleEABoundaryFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA_ADAt(...)\n"
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
" is not implemented for this class.");
}
@@ -919,7 +889,7 @@ void BoundaryMassIntegrator::AssembleFaceMatrix(
{
int order = 2 * el1.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
ir = &IntRules.Get(Trans.FaceGeom, order);
}
elmat = 0.0;
@@ -930,11 +900,11 @@ void BoundaryMassIntegrator::AssembleFaceMatrix(
Trans.Loc1.Transform(ip, eip);
el1.CalcShape(eip, shape);
Trans.SetIntPoint(&ip);
w = Trans.Weight() * ip.weight;
Trans.Face->SetIntPoint(&ip);
w = Trans.Face->Weight() * ip.weight;
if (Q)
{
w *= Q -> Eval(Trans, ip);
w *= Q -> Eval(*Trans.Face, ip);
}
AddMult_a_VVt(w, shape, elmat);
@@ -2004,7 +1974,7 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
D.SetSize(VQ ? VQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
DenseMatrix tmp(test_vshape.Height(), K.Width());
DenseMatrix tmp(trial_vshape.Height(), K.Width());
elmat.SetSize (test_dof, trial_dof);
@@ -2565,7 +2535,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
{
order++;
}
ir = &IntRules.Get(Trans.GetGeometryType(), order);
ir = &IntRules.Get(Trans.FaceGeom, order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
@@ -2579,7 +2549,8 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
}
el1.CalcShape(eip1, shape1);
Trans.SetIntPoint(&ip);
Trans.Face->SetIntPoint(&ip);
Trans.Elem1->SetIntPoint(&eip1);
u->Eval(vu, *Trans.Elem1, eip1);
@@ -2589,7 +2560,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
}
else
{
CalcOrtho(Trans.Jacobian(), nor);
CalcOrtho(Trans.Face->Jacobian(), nor);
}
un = vu * nor;
@@ -2604,6 +2575,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
double rho_p;
if (un >= 0.0 && ndof2)
{
Trans.Elem2->SetIntPoint(&eip2);
rho_p = rho->Eval(*Trans.Elem2, eip2);
}
else
@@ -2719,7 +2691,7 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
{
order = 2*el1.GetOrder();
}
ir = &IntRules.Get(Trans.GetGeometryType(), order);
ir = &IntRules.Get(Trans.FaceGeom, order);
}
// assemble: < {(Q \nabla u).n},[v] > --> elmat
@@ -2730,18 +2702,19 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
IntegrationPoint eip1, eip2;
Trans.Loc1.Transform(ip, eip1);
Trans.SetIntPoint(&ip);
Trans.Face->SetIntPoint(&ip);
if (dim == 1)
{
nor(0) = 2*eip1.x - 1.0;
}
else
{
CalcOrtho(Trans.Jacobian(), nor);
CalcOrtho(Trans.Face->Jacobian(), nor);
}
el1.CalcShape(eip1, shape1);
el1.CalcDShape(eip1, dshape1);
Trans.Elem1->SetIntPoint(&eip1);
w = ip.weight/Trans.Elem1->Weight();
if (ndof2)
{
@@ -2790,6 +2763,7 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
Trans.Loc2.Transform(ip, eip2);
el2.CalcShape(eip2, shape2);
el2.CalcDShape(eip2, dshape2);
Trans.Elem2->SetIntPoint(&eip2);
w = ip.weight/2/Trans.Elem2->Weight();
if (!MQ)
{
@@ -2999,7 +2973,7 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
{
// a simple choice for the integration order; is this OK?
const int order = 2 * max(el1.GetOrder(), ndofs2 ? el2.GetOrder() : 0);
ir = &IntRules.Get(Trans.GetGeometryType(), order);
ir = &IntRules.Get(Trans.FaceGeom, order);
}
for (int pind = 0; pind < ir->GetNPoints(); ++pind)
@@ -3007,7 +2981,8 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
const IntegrationPoint &ip = ir->IntPoint(pind);
IntegrationPoint eip1, eip2; // integration point in the reference space
Trans.Loc1.Transform(ip, eip1);
Trans.SetIntPoint(&ip);
Trans.Face->SetIntPoint(&ip);
Trans.Elem1->SetIntPoint(&eip1);
el1.CalcShape(eip1, shape1);
el1.CalcDShape(eip1, dshape1);
@@ -3021,13 +2996,14 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
}
else
{
CalcOrtho(Trans.Jacobian(), nor);
CalcOrtho(Trans.Face->Jacobian(), nor);
}
double w, wLM;
if (ndofs2)
{
Trans.Loc2.Transform(ip, eip2);
Trans.Elem2->SetIntPoint(&eip2);
el2.CalcShape(eip2, shape2);
el2.CalcDShape(eip2, dshape2);
CalcAdjugate(Trans.Elem2->Jacobian(), adjJ);
@@ -3157,9 +3133,9 @@ void TraceJumpIntegrator::AssembleFaceMatrix(
order += trial_face_fe.GetOrder();
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
{
order += Trans.OrderW();
order += Trans.Face->OrderW();
}
ir = &IntRules.Get(Trans.GetGeometryType(), order);
ir = &IntRules.Get(Trans.FaceGeom, order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
@@ -3167,21 +3143,23 @@ void TraceJumpIntegrator::AssembleFaceMatrix(
const IntegrationPoint &ip = ir->IntPoint(p);
IntegrationPoint eip1, eip2;
// Trace finite element shape function
Trans.SetIntPoint(&ip);
Trans.Face->SetIntPoint(&ip);
trial_face_fe.CalcShape(ip, face_shape);
// Side 1 finite element shape function
Trans.Loc1.Transform(ip, eip1);
test_fe1.CalcShape(eip1, shape1);
Trans.Elem1->SetIntPoint(&eip1);
if (ndof2)
{
// Side 2 finite element shape function
Trans.Loc2.Transform(ip, eip2);
test_fe2.CalcShape(eip2, shape2);
Trans.Elem2->SetIntPoint(&eip2);
}
w = ip.weight;
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
{
w *= Trans.Weight();
w *= Trans.Face->Weight();
}
face_shape *= w;
for (i = 0; i < ndof1; i++)
@@ -3246,7 +3224,7 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
order = test_fe1.GetOrder() - 1;
}
order += trial_face_fe.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
ir = &IntRules.Get(Trans.FaceGeom, order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
+1 -70
View File
@@ -57,9 +57,6 @@ public:
/// Assemble diagonal and add it to Vector @a diag.
virtual void AssembleDiagonalPA(Vector &diag);
/// Assemble diagonal of ADA^T (A is this integrator) and add it to @a diag.
virtual void AssembleDiagonalPA_ADAt(const Vector &D, Vector &diag);
/// Method for partially assembled action.
/** Perform the action of integrator on the input @a x and add the result to
the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
@@ -78,22 +75,6 @@ public:
called. */
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
/// Method defining element assembly.
/** The result of the element assembly is added and stored in the @a emat
Vector. */
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
/** Used with BilinearFormIntegrators that have different spaces. */
// virtual void AssembleEA(const FiniteElementSpace &trial_fes,
// const FiniteElementSpace &test_fes,
// Vector &emat);
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext);
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
Vector &ea_data_bdr);
/// Given a particular Finite Element computes the element matrix elmat.
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -253,15 +234,6 @@ public:
bfi->AddMultTransposePA(x, y);
}
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext);
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
Vector &ea_data_bdr);
virtual ~TransposeIntegrator() { if (own_bfi) { delete bfi; } }
};
@@ -1913,8 +1885,6 @@ public:
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AddMultPA(const Vector&, Vector&) const;
@@ -1988,8 +1958,6 @@ public:
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AddMultPA(const Vector&, Vector&) const;
@@ -2043,8 +2011,6 @@ public:
virtual void AssemblePA(const FiniteElementSpace&);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &el,
@@ -2144,25 +2110,11 @@ class VectorFEDivergenceIntegrator : public BilinearFormIntegrator
protected:
Coefficient *Q;
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector&, Vector&) const;
virtual void AddMultTransposePA(const Vector&, Vector&) const;
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape, shape;
#endif
// PA extension
Vector pa_data;
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
const DofToQuad *L2mapsO; ///< Not owned. DOF-to-quad map, open.
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
int dim, ne, dofs1D, L2dofs1D, quad1D;
public:
VectorFEDivergenceIntegrator() { Q = NULL; }
VectorFEDivergenceIntegrator(Coefficient &q) { Q = &q; }
@@ -2173,8 +2125,6 @@ public:
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssembleDiagonalPA_ADAt(const Vector &D, Vector &diag);
};
@@ -2358,7 +2308,7 @@ protected:
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D, fetype;
int dim, ne, nq, dofs1D, quad1D;
public:
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
@@ -2437,23 +2387,11 @@ class DivDivIntegrator: public BilinearFormIntegrator
protected:
Coefficient *Q;
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AssembleDiagonalPA(Vector& diag);
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape;
#endif
// PA extension
Vector pa_data;
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
const GeometricFactors *geom; ///< Not owned
int dim, ne, dofs1D, quad1D;
public:
DivDivIntegrator() { Q = NULL; }
DivDivIntegrator(Coefficient &q) : Q(&q) { }
@@ -2606,13 +2544,6 @@ public:
virtual void AddMultPA(const Vector&, Vector&) const;
virtual void AssembleEAInteriorFaces(const FiniteElementSpace& fes,
Vector &ea_data_int,
Vector &ea_data_ext);
virtual void AssembleEABoundaryFaces(const FiniteElementSpace& fes,
Vector &ea_data_bdr);
static const IntegrationRule &GetRule(Geometry::Type geom, int order,
FaceElementTransformations &T);
-258
View File
@@ -1,258 +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 "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
template<int T_D1D = 0, int T_Q1D = 0>
static void EAConvectionAssemble1D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto A = Reshape(eadata.Write(), D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_Gi[MQ1];
double r_Bj[MQ1];
for (int q = 0; q < Q1D; q++)
{
r_Gi[q] = G(q,MFEM_THREAD_ID(x));
r_Bj[q] = B(q,MFEM_THREAD_ID(y));
}
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(j1,y,D1D)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
val += r_Bj[k1] * D(k1, e) * r_Gi[k1];
}
A(i1, j1, e) = val;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EAConvectionAssemble2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, NE);
auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
double r_G[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
r_G[q][d] = G(q,d);
}
}
MFEM_SHARED double s_D[MQ1][MQ1][2];
MFEM_FOREACH_THREAD(k1,x,Q1D)
{
MFEM_FOREACH_THREAD(k2,y,Q1D)
{
s_D[k1][k2][0] = D(k1,k2,0,e);
s_D[k1][k2][1] = D(k1,k2,1,e);
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
val += (r_G[k1][i1] * r_B[k2][i2] * s_D[k1][k2][0]
+ r_B[k1][i1] * r_G[k2][i2] * s_D[k1][k2][1])
* r_B[k1][j1]* r_B[k2][j2];
}
}
A(i1, i2, j1, j2, e) = val;
}
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EAConvectionAssemble3D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 3, NE);
auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
double r_G[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
r_G[q][d] = G(q,d);
}
}
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
MFEM_FOREACH_THREAD(i3,z,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
for (int j3 = 0; j3 < D1D; ++j3)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
for (int k3 = 0; k3 < Q1D; ++k3)
{
double D0 = D(k1,k2,k3,0,e);
double D1 = D(k1,k2,k3,1,e);
double D2 = D(k1,k2,k3,2,e);
val += (r_G[k1][i1] * r_B[k2][i2] * r_B[k3][i3] * D0
+ r_B[k1][i1] * r_G[k2][i2] * r_B[k3][i3] * D1
+ r_B[k1][i1] * r_B[k2][i2] * r_G[k3][i3] * D2)
* r_B[k1][j1] * r_B[k2][j2] * r_B[k3][j3];
}
}
}
A(i1, i2, i3, j1, j2, j3, e) = val;
}
}
}
}
}
}
});
}
void ConvectionIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &ea_data)
{
AssemblePA(fes);
const int ne = fes.GetMesh()->GetNE();
const Array<double> &B = maps->B;
const Array<double> &G = maps->G;
if (dim == 1)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EAConvectionAssemble1D<2,2>(ne,B,G,pa_data,ea_data);
case 0x33: return EAConvectionAssemble1D<3,3>(ne,B,G,pa_data,ea_data);
case 0x44: return EAConvectionAssemble1D<4,4>(ne,B,G,pa_data,ea_data);
case 0x55: return EAConvectionAssemble1D<5,5>(ne,B,G,pa_data,ea_data);
case 0x66: return EAConvectionAssemble1D<6,6>(ne,B,G,pa_data,ea_data);
case 0x77: return EAConvectionAssemble1D<7,7>(ne,B,G,pa_data,ea_data);
case 0x88: return EAConvectionAssemble1D<8,8>(ne,B,G,pa_data,ea_data);
case 0x99: return EAConvectionAssemble1D<9,9>(ne,B,G,pa_data,ea_data);
default: return EAConvectionAssemble1D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
}
}
else if (dim == 2)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EAConvectionAssemble2D<2,2>(ne,B,G,pa_data,ea_data);
case 0x33: return EAConvectionAssemble2D<3,3>(ne,B,G,pa_data,ea_data);
case 0x44: return EAConvectionAssemble2D<4,4>(ne,B,G,pa_data,ea_data);
case 0x55: return EAConvectionAssemble2D<5,5>(ne,B,G,pa_data,ea_data);
case 0x66: return EAConvectionAssemble2D<6,6>(ne,B,G,pa_data,ea_data);
case 0x77: return EAConvectionAssemble2D<7,7>(ne,B,G,pa_data,ea_data);
case 0x88: return EAConvectionAssemble2D<8,8>(ne,B,G,pa_data,ea_data);
case 0x99: return EAConvectionAssemble2D<9,9>(ne,B,G,pa_data,ea_data);
default: return EAConvectionAssemble2D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
}
}
else if (dim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x23: return EAConvectionAssemble3D<2,3>(ne,B,G,pa_data,ea_data);
case 0x34: return EAConvectionAssemble3D<3,4>(ne,B,G,pa_data,ea_data);
case 0x45: return EAConvectionAssemble3D<4,5>(ne,B,G,pa_data,ea_data);
case 0x56: return EAConvectionAssemble3D<5,6>(ne,B,G,pa_data,ea_data);
case 0x67: return EAConvectionAssemble3D<6,7>(ne,B,G,pa_data,ea_data);
case 0x78: return EAConvectionAssemble3D<7,8>(ne,B,G,pa_data,ea_data);
case 0x89: return EAConvectionAssemble3D<8,9>(ne,B,G,pa_data,ea_data);
default: return EAConvectionAssemble3D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
}
-414
View File
@@ -1,414 +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 "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
static void EADGTraceAssemble1DInt(const int NF,
const Array<double> &basis,
const Vector &padata,
Vector &eadata_int,
Vector &eadata_ext)
{
auto D = Reshape(padata.Read(), 2, 2, NF);
auto A_int = Reshape(eadata_int.ReadWrite(), 2, NF);
auto A_ext = Reshape(eadata_ext.ReadWrite(), 2, NF);
MFEM_FORALL(f, NF,
{
double val_int0, val_int1, val_ext01, val_ext10;
val_int0 = D(0, 0, f);
val_ext10 = D(1, 0, f);
val_ext01 = D(0, 1, f);
val_int1 = D(1, 1, f);
A_int(0, f) += val_int0;
A_int(1, f) += val_int1;
A_ext(0, f) += val_ext01;
A_ext(1, f) += val_ext10;
});
}
static void EADGTraceAssemble1DBdr(const int NF,
const Array<double> &basis,
const Vector &padata,
Vector &eadata_bdr)
{
auto D = Reshape(padata.Read(), 2, 2, NF);
auto A_bdr = Reshape(eadata_bdr.ReadWrite(), NF);
MFEM_FORALL(f, NF,
{
A_bdr(f) += D(0, 0, f);
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EADGTraceAssemble2DInt(const int NF,
const Array<double> &basis,
const Vector &padata,
Vector &eadata_int,
Vector &eadata_ext,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, 2, 2, NF);
auto A_int = Reshape(eadata_int.ReadWrite(), D1D, D1D, 2, NF);
auto A_ext = Reshape(eadata_ext.ReadWrite(), D1D, D1D, 2, NF);
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(j1,y,D1D)
{
double val_int0 = 0.0;
double val_int1 = 0.0;
double val_ext01 = 0.0;
double val_ext10 = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
val_int0 += B(k1,i1) * B(k1,j1) * D(k1, 0, 0, f);
val_ext01 += B(k1,i1) * B(k1,j1) * D(k1, 0, 1, f);
val_ext10 += B(k1,i1) * B(k1,j1) * D(k1, 1, 0, f);
val_int1 += B(k1,i1) * B(k1,j1) * D(k1, 1, 1, f);
}
A_int(i1, j1, 0, f) += val_int0;
A_int(i1, j1, 1, f) += val_int1;
A_ext(i1, j1, 0, f) += val_ext01;
A_ext(i1, j1, 1, f) += val_ext10;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EADGTraceAssemble2DBdr(const int NF,
const Array<double> &basis,
const Vector &padata,
Vector &eadata_bdr,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, 2, 2, NF);
auto A_bdr = Reshape(eadata_bdr.ReadWrite(), D1D, D1D, NF);
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(j1,y,D1D)
{
double val_bdr = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
val_bdr += B(k1,i1) * B(k1,j1) * D(k1, 0, 0, f);
}
A_bdr(i1, j1, f) += val_bdr;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EADGTraceAssemble3DInt(const int NF,
const Array<double> &basis,
const Vector &padata,
Vector &eadata_int,
Vector &eadata_ext,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, 2, NF);
auto A_int = Reshape(eadata_int.ReadWrite(), D1D, D1D, D1D, D1D, 2, NF);
auto A_ext = Reshape(eadata_ext.ReadWrite(), D1D, D1D, D1D, D1D, 2, NF);
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
}
}
MFEM_SHARED double s_D[MQ1][MQ1][2][2];
for (int i=0; i < 2; i++)
{
for (int j=0; j < 2; j++)
{
MFEM_FOREACH_THREAD(k1,x,Q1D)
{
MFEM_FOREACH_THREAD(k2,y,Q1D)
{
s_D[k1][k2][i][j] = D(k1,k2,i,j,f);
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
double val_int0 = 0.0;
double val_int1 = 0.0;
double val_ext01 = 0.0;
double val_ext10 = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
val_int0 += r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* s_D[k1][k2][0][0];
val_int1 += r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* s_D[k1][k2][1][1];
val_ext01+= r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* s_D[k1][k2][0][1];
val_ext10+= r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* s_D[k1][k2][1][0];
}
}
A_int(i1, i2, j1, j2, 0, f) += val_int0;
A_int(i1, i2, j1, j2, 1, f) += val_int1;
A_ext(i1, i2, j1, j2, 0, f) += val_ext01;
A_ext(i1, i2, j1, j2, 1, f) += val_ext10;
}
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EADGTraceAssemble3DBdr(const int NF,
const Array<double> &basis,
const Vector &padata,
Vector &eadata_bdr,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, 2, NF);
auto A_bdr = Reshape(eadata_bdr.ReadWrite(), D1D, D1D, D1D, D1D, NF);
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
}
}
MFEM_SHARED double s_D[MQ1][MQ1][2][2];
for (int i=0; i < 2; i++)
{
for (int j=0; j < 2; j++)
{
MFEM_FOREACH_THREAD(k1,x,Q1D)
{
MFEM_FOREACH_THREAD(k2,y,Q1D)
{
s_D[k1][k2][i][j] = D(k1,k2,i,j,f);
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
double val_bdr = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
val_bdr += r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* s_D[k1][k2][0][0];
}
}
A_bdr(i1, i2, j1, j2, f) += val_bdr;
}
}
}
}
});
}
void DGTraceIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace& fes,
Vector &ea_data_int,
Vector &ea_data_ext)
{
SetupPA(fes, FaceType::Interior);
nf = fes.GetNFbyType(FaceType::Interior);
if (nf==0) { return; }
const Array<double> &B = maps->B;
if (dim == 1)
{
return EADGTraceAssemble1DInt(nf,B,pa_data,ea_data_int,ea_data_ext);
}
else if (dim == 2)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22:
return EADGTraceAssemble2DInt<2,2>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x33:
return EADGTraceAssemble2DInt<3,3>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x44:
return EADGTraceAssemble2DInt<4,4>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x55:
return EADGTraceAssemble2DInt<5,5>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x66:
return EADGTraceAssemble2DInt<6,6>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x77:
return EADGTraceAssemble2DInt<7,7>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x88:
return EADGTraceAssemble2DInt<8,8>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x99:
return EADGTraceAssemble2DInt<9,9>(nf,B,pa_data,ea_data_int,
ea_data_ext);
default:
return EADGTraceAssemble2DInt(nf,B,pa_data,ea_data_int,
ea_data_ext,dofs1D,quad1D);
}
}
else if (dim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x23:
return EADGTraceAssemble3DInt<2,3>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x34:
return EADGTraceAssemble3DInt<3,4>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x45:
return EADGTraceAssemble3DInt<4,5>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x56:
return EADGTraceAssemble3DInt<5,6>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x67:
return EADGTraceAssemble3DInt<6,7>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x78:
return EADGTraceAssemble3DInt<7,8>(nf,B,pa_data,ea_data_int,
ea_data_ext);
case 0x89:
return EADGTraceAssemble3DInt<8,9>(nf,B,pa_data,ea_data_int,
ea_data_ext);
default:
return EADGTraceAssemble3DInt(nf,B,pa_data,ea_data_int,
ea_data_ext,dofs1D,quad1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
void DGTraceIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace& fes,
Vector &ea_data_bdr)
{
SetupPA(fes, FaceType::Boundary);
nf = fes.GetNFbyType(FaceType::Boundary);
if (nf==0) { return; }
const Array<double> &B = maps->B;
if (dim == 1)
{
return EADGTraceAssemble1DBdr(nf,B,pa_data,ea_data_bdr);
}
else if (dim == 2)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EADGTraceAssemble2DBdr<2,2>(nf,B,pa_data,ea_data_bdr);
case 0x33: return EADGTraceAssemble2DBdr<3,3>(nf,B,pa_data,ea_data_bdr);
case 0x44: return EADGTraceAssemble2DBdr<4,4>(nf,B,pa_data,ea_data_bdr);
case 0x55: return EADGTraceAssemble2DBdr<5,5>(nf,B,pa_data,ea_data_bdr);
case 0x66: return EADGTraceAssemble2DBdr<6,6>(nf,B,pa_data,ea_data_bdr);
case 0x77: return EADGTraceAssemble2DBdr<7,7>(nf,B,pa_data,ea_data_bdr);
case 0x88: return EADGTraceAssemble2DBdr<8,8>(nf,B,pa_data,ea_data_bdr);
case 0x99: return EADGTraceAssemble2DBdr<9,9>(nf,B,pa_data,ea_data_bdr);
default:
return EADGTraceAssemble2DBdr(nf,B,pa_data,ea_data_bdr,dofs1D,quad1D);
}
}
else if (dim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x23: return EADGTraceAssemble3DBdr<2,3>(nf,B,pa_data,ea_data_bdr);
case 0x34: return EADGTraceAssemble3DBdr<3,4>(nf,B,pa_data,ea_data_bdr);
case 0x45: return EADGTraceAssemble3DBdr<4,5>(nf,B,pa_data,ea_data_bdr);
case 0x56: return EADGTraceAssemble3DBdr<5,6>(nf,B,pa_data,ea_data_bdr);
case 0x67: return EADGTraceAssemble3DBdr<6,7>(nf,B,pa_data,ea_data_bdr);
case 0x78: return EADGTraceAssemble3DBdr<7,8>(nf,B,pa_data,ea_data_bdr);
case 0x89: return EADGTraceAssemble3DBdr<8,9>(nf,B,pa_data,ea_data_bdr);
default:
return EADGTraceAssemble3DBdr(nf,B,pa_data,ea_data_bdr,dofs1D,quad1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
}
-275
View File
@@ -1,275 +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 "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
template<int T_D1D = 0, int T_Q1D = 0>
static void EADiffusionAssemble1D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto A = Reshape(eadata.Write(), D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_Gi[MQ1];
double r_Gj[MQ1];
for (int q = 0; q < Q1D; q++)
{
r_Gi[q] = G(q,MFEM_THREAD_ID(x));
r_Gj[q] = G(q,MFEM_THREAD_ID(y));
}
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(j1,y,D1D)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
val += r_Gj[k1] * D(k1, e) * r_Gi[k1];
}
A(i1, j1, e) = val;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EADiffusionAssemble2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 3, NE);
auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
double r_G[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
r_G[q][d] = G(q,d);
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
double bgi = r_G[k1][i1] * r_B[k2][i2];
double gbi = r_B[k1][i1] * r_G[k2][i2];
double bgj = r_G[k1][j1] * r_B[k2][j2];
double gbj = r_B[k1][j1] * r_G[k2][j2];
double D00 = D(k1,k2,0,e);
double D10 = D(k1,k2,1,e);
double D01 = D10;
double D11 = D(k1,k2,2,e);
val += bgi * D00 * bgj
+ gbi * D01 * bgj
+ bgi * D10 * gbj
+ gbi * D11 * gbj;
}
}
A(i1, i2, j1, j2, e) = val;
}
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EADiffusionAssemble3D(const int NE,
const Array<double> &g,
const Array<double> &b,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 6, NE);
auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
double r_G[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
r_G[q][d] = G(q,d);
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
MFEM_FOREACH_THREAD(i3,z,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
for (int j3 = 0; j3 < D1D; ++j3)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
for (int k3 = 0; k3 < Q1D; ++k3)
{
double bbgi = r_G[k1][i1] * r_B[k2][i2] * r_B[k3][i3];
double bgbi = r_B[k1][i1] * r_G[k2][i2] * r_B[k3][i3];
double gbbi = r_B[k1][i1] * r_B[k2][i2] * r_G[k3][i3];
double bbgj = r_G[k1][j1] * r_B[k2][j2] * r_B[k3][j3];
double bgbj = r_B[k1][j1] * r_G[k2][j2] * r_B[k3][j3];
double gbbj = r_B[k1][j1] * r_B[k2][j2] * r_G[k3][j3];
double D00 = D(k1,k2,k3,0,e);
double D10 = D(k1,k2,k3,1,e);
double D20 = D(k1,k2,k3,2,e);
double D01 = D10;
double D11 = D(k1,k2,k3,3,e);
double D21 = D(k1,k2,k3,4,e);
double D02 = D20;
double D12 = D21;
double D22 = D(k1,k2,k3,5,e);
val += bbgi * D00 * bbgj
+ bgbi * D10 * bbgj
+ gbbi * D20 * bbgj
+ bbgi * D01 * bgbj
+ bgbi * D11 * bgbj
+ gbbi * D21 * bgbj
+ bbgi * D02 * gbbj
+ bgbi * D12 * gbbj
+ gbbi * D22 * gbbj;
}
}
}
A(i1, i2, i3, j1, j2, j3, e) = val;
}
}
}
}
}
}
});
}
void DiffusionIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &ea_data)
{
AssemblePA(fes);
const int ne = fes.GetMesh()->GetNE();
const Array<double> &B = maps->B;
const Array<double> &G = maps->G;
if (dim == 1)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EADiffusionAssemble1D<2,2>(ne,B,G,pa_data,ea_data);
case 0x33: return EADiffusionAssemble1D<3,3>(ne,B,G,pa_data,ea_data);
case 0x44: return EADiffusionAssemble1D<4,4>(ne,B,G,pa_data,ea_data);
case 0x55: return EADiffusionAssemble1D<5,5>(ne,B,G,pa_data,ea_data);
case 0x66: return EADiffusionAssemble1D<6,6>(ne,B,G,pa_data,ea_data);
case 0x77: return EADiffusionAssemble1D<7,7>(ne,B,G,pa_data,ea_data);
case 0x88: return EADiffusionAssemble1D<8,8>(ne,B,G,pa_data,ea_data);
case 0x99: return EADiffusionAssemble1D<9,9>(ne,B,G,pa_data,ea_data);
default: return EADiffusionAssemble1D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
}
}
else if (dim == 2)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EADiffusionAssemble2D<2,2>(ne,B,G,pa_data,ea_data);
case 0x33: return EADiffusionAssemble2D<3,3>(ne,B,G,pa_data,ea_data);
case 0x44: return EADiffusionAssemble2D<4,4>(ne,B,G,pa_data,ea_data);
case 0x55: return EADiffusionAssemble2D<5,5>(ne,B,G,pa_data,ea_data);
case 0x66: return EADiffusionAssemble2D<6,6>(ne,B,G,pa_data,ea_data);
case 0x77: return EADiffusionAssemble2D<7,7>(ne,B,G,pa_data,ea_data);
case 0x88: return EADiffusionAssemble2D<8,8>(ne,B,G,pa_data,ea_data);
case 0x99: return EADiffusionAssemble2D<9,9>(ne,B,G,pa_data,ea_data);
default: return EADiffusionAssemble2D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
}
}
else if (dim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x23: return EADiffusionAssemble3D<2,3>(ne,B,G,pa_data,ea_data);
case 0x34: return EADiffusionAssemble3D<3,4>(ne,B,G,pa_data,ea_data);
case 0x45: return EADiffusionAssemble3D<4,5>(ne,B,G,pa_data,ea_data);
case 0x56: return EADiffusionAssemble3D<5,6>(ne,B,G,pa_data,ea_data);
case 0x67: return EADiffusionAssemble3D<6,7>(ne,B,G,pa_data,ea_data);
case 0x78: return EADiffusionAssemble3D<7,8>(ne,B,G,pa_data,ea_data);
case 0x89: return EADiffusionAssemble3D<8,9>(ne,B,G,pa_data,ea_data);
default: return EADiffusionAssemble3D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
}
+240 -77
View File
@@ -24,12 +24,12 @@ constexpr int HCURL_MAX_D1D = 5;
constexpr int HCURL_MAX_Q1D = 6;
// PA H(curl) Mass Assemble 2D kernel
void PAHcurlSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op)
static void PAHcurlSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op)
{
const int NQ = Q1D*Q1D;
auto W = w.Read();
@@ -55,12 +55,12 @@ void PAHcurlSetup2D(const int Q1D,
}
// PA H(curl) Mass Assemble 3D kernel
void PAHcurlSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op)
static void PAHcurlSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
auto W = w.Read();
@@ -106,16 +106,78 @@ void PAHcurlSetup3D(const int Q1D,
});
}
void PAHcurlMassApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement *fel = fes.GetFE(0);
const VectorTensorFiniteElement *el =
dynamic_cast<const VectorTensorFiniteElement*>(fel);
MFEM_VERIFY(el != NULL, "Only VectorTensorFiniteElement is supported!");
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(*el, *el,
*mesh->GetElementTransformation(0));
const int dims = el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
quad1D = mapsC->nqpt;
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
Vector coeff(ne * nq);
coeff = 1.0;
if (Q)
{
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
coeff[p + (e * nq)] = Q->Eval(*tr, ir->IntPoint(p));
}
}
}
if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
{
PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
{
PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
static void PAHcurlMassApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
{
constexpr static int VDIM = 2;
@@ -232,13 +294,13 @@ void PAHcurlMassApply2D(const int D1D,
}); // end of element loop
}
void PAHcurlMassAssembleDiagonal2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag)
static void PAHcurlMassAssembleDiagonal2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag)
{
constexpr static int VDIM = 2;
@@ -286,17 +348,15 @@ void PAHcurlMassAssembleDiagonal2D(const int D1D,
}); // end of element loop
}
void PAHcurlMassAssembleDiagonal3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag)
template<int MAX_D1D = HCURL_MAX_D1D, int MAX_Q1D = HCURL_MAX_Q1D>
static void PAHcurlMassAssembleDiagonal3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag)
{
constexpr static int MAX_D1D = HCURL_MAX_D1D;
constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
constexpr static int VDIM = 3;
@@ -356,20 +416,28 @@ void PAHcurlMassAssembleDiagonal3D(const int D1D,
}); // end of element loop
}
void PAHcurlMassApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
{
constexpr static int MAX_D1D = HCURL_MAX_D1D;
constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
if (dim == 3)
PAHcurlMassAssembleDiagonal3D(dofs1D, quad1D, ne,
mapsO->B, mapsC->B, pa_data, diag);
else
PAHcurlMassAssembleDiagonal2D(dofs1D, quad1D, ne,
mapsO->B, mapsC->B, pa_data, diag);
}
template<int MAX_D1D = HCURL_MAX_D1D, int MAX_Q1D = HCURL_MAX_Q1D>
static void PAHcurlMassApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
{
MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
constexpr static int VDIM = 3;
@@ -547,6 +615,20 @@ void PAHcurlMassApply3D(const int D1D,
}); // end of element loop
}
void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
PAHcurlMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
mapsC->Bt, pa_data, x, y);
}
else
{
PAHcurlMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
mapsC->Bt, pa_data, x, y);
}
}
// PA H(curl) curl-curl assemble 2D kernel
static void PACurlCurlSetup2D(const int Q1D,
const int NE,
@@ -1596,25 +1678,92 @@ void CurlCurlIntegrator::AssembleDiagonalPA(Vector& diag)
}
}
void MixedVectorGradientIntegrator::AssemblePA(const FiniteElementSpace
&trial_fes,
const FiniteElementSpace &test_fes)
{
// Assumes tensor-product elements, with a vector test space and H^1 trial space.
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetFE(0);
const FiniteElement *test_fel = test_fes.GetFE(0);
const NodalTensorFiniteElement *trial_el =
dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
const VectorTensorFiniteElement *test_el =
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
*mesh->GetElementTransformation(0));
const int dims = trial_el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
ne = trial_fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
quad1D = mapsC->nqpt;
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
Vector coeff(ne * nq);
coeff = 1.0;
if (Q)
{
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
coeff[p + (e * nq)] = Q->Eval(*tr, ir->IntPoint(p));
}
}
}
// Use the same setup functions as VectorFEMassIntegrator.
if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
{
PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
{
PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
// Apply to x corresponding to DOF's in H^1 (trial), whose gradients are integrated
// against H(curl) test functions corresponding to y.
void PAHcurlH1Apply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bc,
const Array<double> &_Gc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
template<int MAX_D1D = HCURL_MAX_D1D, int MAX_Q1D = HCURL_MAX_Q1D>
static void PAHcurlH1Apply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bc,
const Array<double> &_Gc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
{
constexpr static int MAX_D1D = HCURL_MAX_D1D;
constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
constexpr static int VDIM = 3;
auto Bc = Reshape(_Bc.Read(), Q1D, D1D);
@@ -1788,16 +1937,16 @@ void PAHcurlH1Apply3D(const int D1D,
// Apply to x corresponding to DOF's in H^1 (trial), whose gradients are integrated
// against H(curl) test functions corresponding to y.
void PAHcurlH1Apply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bc,
const Array<double> &_Gc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
static void PAHcurlH1Apply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bc,
const Array<double> &_Gc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y)
{
constexpr static int VDIM = 2;
@@ -1908,4 +2057,18 @@ void PAHcurlH1Apply2D(const int D1D,
}); // end of element loop
}
void MixedVectorGradientIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
PAHcurlH1Apply3D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
mapsO->Bt, mapsC->Bt, pa_data, x, y);
else if (dim == 2)
PAHcurlH1Apply2D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
mapsO->Bt, mapsC->Bt, pa_data, x, y);
else
{
MFEM_ABORT("Unsupported dimension!");
}
}
} // namespace mfem
File diff suppressed because it is too large Load Diff
-255
View File
@@ -1,255 +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 "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
template<int T_D1D = 0, int T_Q1D = 0>
static void EAMassAssemble1D(const int NE,
const Array<double> &basis,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto M = Reshape(eadata.Write(), D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_Bi[MQ1];
double r_Bj[MQ1];
for (int q = 0; q < Q1D; q++)
{
r_Bi[q] = B(q,MFEM_THREAD_ID(x));
r_Bj[q] = B(q,MFEM_THREAD_ID(y));
}
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(j1,y,D1D)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
val += r_Bi[k1] * r_Bj[k1] * D(k1, e);
}
M(i1, j1, e) = val;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EAMassAssemble2D(const int NE,
const Array<double> &basis,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, NE);
auto M = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
}
}
MFEM_SHARED double s_D[MQ1][MQ1];
MFEM_FOREACH_THREAD(k1,x,Q1D)
{
MFEM_FOREACH_THREAD(k2,y,Q1D)
{
s_D[k1][k2] = D(k1,k2,e);
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
val += r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* s_D[k1][k2];
}
}
M(i1, i2, j1, j2, e) = val;
}
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void EAMassAssemble3D(const int NE,
const Array<double> &basis,
const Vector &padata,
Vector &eadata,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, NE);
auto M = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double r_B[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
}
}
MFEM_SHARED double s_D[MQ1][MQ1][MQ1];
MFEM_FOREACH_THREAD(k1,x,Q1D)
{
MFEM_FOREACH_THREAD(k2,y,Q1D)
{
MFEM_FOREACH_THREAD(k3,z,Q1D)
{
s_D[k1][k2][k3] = D(k1,k2,k3,e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i1,x,D1D)
{
MFEM_FOREACH_THREAD(i2,y,D1D)
{
MFEM_FOREACH_THREAD(i3,z,D1D)
{
for (int j1 = 0; j1 < D1D; ++j1)
{
for (int j2 = 0; j2 < D1D; ++j2)
{
for (int j3 = 0; j3 < D1D; ++j3)
{
double val = 0.0;
for (int k1 = 0; k1 < Q1D; ++k1)
{
for (int k2 = 0; k2 < Q1D; ++k2)
{
for (int k3 = 0; k3 < Q1D; ++k3)
{
val += r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* r_B[k3][i3] * r_B[k3][j3]
* s_D[k1][k2][k3];
}
}
}
M(i1, i2, i3, j1, j2, j3, e) = val;
}
}
}
}
}
}
});
}
void MassIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &ea_data)
{
AssemblePA(fes);
const int ne = fes.GetMesh()->GetNE();
const Array<double> &B = maps->B;
if (dim == 1)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EAMassAssemble1D<2,2>(ne,B,pa_data,ea_data);
case 0x33: return EAMassAssemble1D<3,3>(ne,B,pa_data,ea_data);
case 0x44: return EAMassAssemble1D<4,4>(ne,B,pa_data,ea_data);
case 0x55: return EAMassAssemble1D<5,5>(ne,B,pa_data,ea_data);
case 0x66: return EAMassAssemble1D<6,6>(ne,B,pa_data,ea_data);
case 0x77: return EAMassAssemble1D<7,7>(ne,B,pa_data,ea_data);
case 0x88: return EAMassAssemble1D<8,8>(ne,B,pa_data,ea_data);
case 0x99: return EAMassAssemble1D<9,9>(ne,B,pa_data,ea_data);
default: return EAMassAssemble1D(ne,B,pa_data,ea_data,dofs1D,quad1D);
}
}
else if (dim == 2)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return EAMassAssemble2D<2,2>(ne,B,pa_data,ea_data);
case 0x33: return EAMassAssemble2D<3,3>(ne,B,pa_data,ea_data);
case 0x44: return EAMassAssemble2D<4,4>(ne,B,pa_data,ea_data);
case 0x55: return EAMassAssemble2D<5,5>(ne,B,pa_data,ea_data);
case 0x66: return EAMassAssemble2D<6,6>(ne,B,pa_data,ea_data);
case 0x77: return EAMassAssemble2D<7,7>(ne,B,pa_data,ea_data);
case 0x88: return EAMassAssemble2D<8,8>(ne,B,pa_data,ea_data);
case 0x99: return EAMassAssemble2D<9,9>(ne,B,pa_data,ea_data);
default: return EAMassAssemble2D(ne,B,pa_data,ea_data,dofs1D,quad1D);
}
}
else if (dim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x23: return EAMassAssemble3D<2,3>(ne,B,pa_data,ea_data);
case 0x34: return EAMassAssemble3D<3,4>(ne,B,pa_data,ea_data);
case 0x45: return EAMassAssemble3D<4,5>(ne,B,pa_data,ea_data);
case 0x56: return EAMassAssemble3D<5,6>(ne,B,pa_data,ea_data);
case 0x67: return EAMassAssemble3D<6,7>(ne,B,pa_data,ea_data);
case 0x78: return EAMassAssemble3D<7,8>(ne,B,pa_data,ea_data);
case 0x89: return EAMassAssemble3D<8,9>(ne,B,pa_data,ea_data);
default: return EAMassAssemble3D(ne,B,pa_data,ea_data,dofs1D,quad1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
}
-103
View File
@@ -1,103 +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 "../general/forall.hpp"
#include "bilininteg.hpp"
namespace mfem
{
void TransposeIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &ea_data)
{
Vector ea_data_tmp(ea_data.Size());
ea_data_tmp = 0.0;
bfi->AssembleEA(fes, ea_data_tmp);
const int ne = fes.GetNE();
if (ne == 0) { return; }
const int dofs = fes.GetFE(0)->GetDof();
auto A = Reshape(ea_data_tmp.Write(), dofs, dofs, ne);
auto AT = Reshape(ea_data.Write(), dofs, dofs, ne);
MFEM_FORALL(e, ne,
{
for (int i = 0; i < dofs; i++)
{
for (int j = 0; j < dofs; j++)
{
const double a = A(i, j, e);
AT(j, i, e) += a;
}
}
});
}
void TransposeIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace& fes,
Vector &ea_data_int,
Vector &ea_data_ext)
{
const int nf = fes.GetNFbyType(FaceType::Interior);
if (nf == 0) { return; }
Vector ea_data_int_tmp(ea_data_int.Size());
Vector ea_data_ext_tmp(ea_data_ext.Size());
ea_data_int_tmp = 0.0;
ea_data_ext_tmp = 0.0;
bfi->AssembleEAInteriorFaces(fes, ea_data_int_tmp, ea_data_ext_tmp);
const int faceDofs = fes.GetTraceElement(0,
fes.GetMesh()->GetFaceBaseGeometry(0))->GetDof();
auto A_int = Reshape(ea_data_int_tmp.Read(), faceDofs, faceDofs, 2, nf);
auto A_ext = Reshape(ea_data_ext_tmp.Read(), faceDofs, faceDofs, 2, nf);
auto AT_int = Reshape(ea_data_int.ReadWrite(), faceDofs, faceDofs, 2, nf);
auto AT_ext = Reshape(ea_data_ext.ReadWrite(), faceDofs, faceDofs, 2, nf);
MFEM_FORALL(f, nf,
{
for (int i = 0; i < faceDofs; i++)
{
for (int j = 0; j < faceDofs; j++)
{
const double a_int0 = A_int(i, j, 0, f);
const double a_int1 = A_int(i, j, 1, f);
const double a_ext0 = A_ext(i, j, 0, f);
const double a_ext1 = A_ext(i, j, 1, f);
AT_int(j, i, 0, f) += a_int0;
AT_int(j, i, 1, f) += a_int1;
AT_ext(j, i, 0, f) += a_ext1;
AT_ext(j, i, 1, f) += a_ext0;
}
}
});
}
void TransposeIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace& fes,
Vector &ea_data_bdr)
{
const int nf = fes.GetNFbyType(FaceType::Boundary);
if (nf == 0) { return; }
Vector ea_data_bdr_tmp(ea_data_bdr.Size());
ea_data_bdr_tmp = 0.0;
bfi->AssembleEABoundaryFaces(fes, ea_data_bdr_tmp);
const int faceDofs = fes.GetTraceElement(0,
fes.GetMesh()->GetFaceBaseGeometry(0))->GetDof();
auto A_bdr = Reshape(ea_data_bdr_tmp.Read(), faceDofs, faceDofs, nf);
auto AT_bdr = Reshape(ea_data_bdr.ReadWrite(), faceDofs, faceDofs, nf);
MFEM_FORALL(f, nf,
{
for (int i = 0; i < faceDofs; i++)
{
for (int j = 0; j < faceDofs; j++)
{
const double a_bdr = A_bdr(i, j, f);
AT_bdr(j, i, f) += a_bdr;
}
}
});
}
}
-379
View File
@@ -1,379 +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"
namespace mfem
{
void PAHcurlSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op);
void PAHcurlSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op);
void PAHcurlMassAssembleDiagonal2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag);
void PAHcurlMassAssembleDiagonal3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag);
void PAHcurlMassApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y);
void PAHcurlMassApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y);
void PAHdivSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op);
void PAHdivSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &_coeff,
Vector &op);
void PAHcurlH1Apply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bc,
const Array<double> &_Gc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y);
void PAHcurlH1Apply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bc,
const Array<double> &_Gc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y);
void PAHdivMassAssembleDiagonal2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag);
void PAHdivMassAssembleDiagonal3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag);
void PAHdivMassApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y);
void PAHdivMassApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Array<double> &_Bot,
const Array<double> &_Bct,
const Vector &_op,
const Vector &_x,
Vector &_y);
void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement *fel = fes.GetFE(0);
const VectorTensorFiniteElement *el =
dynamic_cast<const VectorTensorFiniteElement*>(fel);
MFEM_VERIFY(el != NULL, "Only VectorTensorFiniteElement is supported!");
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(*el, *el,
*mesh->GetElementTransformation(0));
const int dims = el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
quad1D = mapsC->nqpt;
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
Vector coeff(ne * nq);
coeff = 1.0;
if (Q)
{
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
coeff[p + (e * nq)] = Q->Eval(*tr, ir->IntPoint(p));
}
}
}
fetype = el->GetDerivType();
if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
{
PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
{
PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (el->GetDerivType() == mfem::FiniteElement::DIV && dim == 3)
{
PAHdivSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (el->GetDerivType() == mfem::FiniteElement::DIV && dim == 2)
{
PAHdivSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
{
if (dim == 3)
{
if (fetype == mfem::FiniteElement::CURL)
{
PAHcurlMassAssembleDiagonal3D(dofs1D, quad1D, ne,
mapsO->B, mapsC->B, pa_data, diag);
}
else if (fetype == mfem::FiniteElement::DIV)
{
PAHdivMassAssembleDiagonal3D(dofs1D, quad1D, ne,
mapsO->B, mapsC->B, pa_data, diag);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
else
{
if (fetype == mfem::FiniteElement::CURL)
{
PAHcurlMassAssembleDiagonal2D(dofs1D, quad1D, ne,
mapsO->B, mapsC->B, pa_data, diag);
}
else if (fetype == mfem::FiniteElement::DIV)
{
PAHdivMassAssembleDiagonal2D(dofs1D, quad1D, ne,
mapsO->B, mapsC->B, pa_data, diag);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
}
void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
if (fetype == mfem::FiniteElement::CURL)
{
PAHcurlMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
mapsC->Bt, pa_data, x, y);
}
else if (fetype == mfem::FiniteElement::DIV)
{
PAHdivMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
mapsC->Bt, pa_data, x, y);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
else
{
if (fetype == mfem::FiniteElement::CURL)
{
PAHcurlMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
mapsC->Bt, pa_data, x, y);
}
else if (fetype == mfem::FiniteElement::DIV)
{
PAHdivMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
mapsC->Bt, pa_data, x, y);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
}
void MixedVectorGradientIntegrator::AssemblePA(const FiniteElementSpace
&trial_fes,
const FiniteElementSpace &test_fes)
{
// Assumes tensor-product elements, with a vector test space and H^1 trial space.
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetFE(0);
const FiniteElement *test_fel = test_fes.GetFE(0);
const NodalTensorFiniteElement *trial_el =
dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
const VectorTensorFiniteElement *test_el =
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
*mesh->GetElementTransformation(0));
const int dims = trial_el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
ne = trial_fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
quad1D = mapsC->nqpt;
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
Vector coeff(ne * nq);
coeff = 1.0;
if (Q)
{
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
coeff[p + (e * nq)] = Q->Eval(*tr, ir->IntPoint(p));
}
}
}
// Use the same setup functions as VectorFEMassIntegrator.
if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
{
PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
{
PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else
{
MFEM_ABORT("Unknown kernel.");
}
}
void MixedVectorGradientIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
PAHcurlH1Apply3D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
mapsO->Bt, mapsC->Bt, pa_data, x, y);
else if (dim == 2)
PAHcurlH1Apply2D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
mapsO->Bt, mapsC->Bt, pa_data, x, y);
else
{
MFEM_ABORT("Unsupported dimension!");
}
}
} // namespace mfem
+26 -66
View File
@@ -49,7 +49,7 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
double GridFunctionCoefficient::Eval (ElementTransformation &T,
const IntegrationPoint &ip)
{
return GridF -> GetValue (T, ip, Component);
return GridF -> GetValue (T.ElementNo, ip, Component);
}
double TransformedCoefficient::Eval(ElementTransformation &T,
@@ -160,13 +160,13 @@ void VectorArrayCoefficient::Eval(Vector &V, ElementTransformation &T,
}
VectorGridFunctionCoefficient::VectorGridFunctionCoefficient (
const GridFunction *gf)
GridFunction *gf)
: VectorCoefficient ((gf) ? gf -> VectorDim() : 0)
{
GridFunc = gf;
}
void VectorGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
void VectorGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
{
GridFunc = gf; vdim = (gf) ? gf -> VectorDim() : 0;
}
@@ -174,7 +174,24 @@ void VectorGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
void VectorGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
GridFunc->GetVectorValue(T, ip, V);
Mesh *mesh = GridFunc->FESpace()->GetMesh();
if (mesh->Dimension() == T.GetDimension())
{
GridFunc->GetVectorValue(T.ElementNo, ip, V);
}
else // Assuming T is a boundary element transformation
{
int el_id, el_info;
mesh->GetBdrElementAdjacentElement(T.ElementNo, el_id, el_info);
IntegrationPointTransformation loc_T;
mesh->GetLocalFaceTransformation(mesh->GetBdrElementType(T.ElementNo),
mesh->GetElementType(el_id),
loc_T.Transf,
el_info);
IntegrationPoint eip;
loc_T.Transform(ip, eip);
GridFunc->GetVectorValue(el_id, eip, V);
}
}
void VectorGridFunctionCoefficient::Eval(
@@ -184,14 +201,14 @@ void VectorGridFunctionCoefficient::Eval(
}
GradientGridFunctionCoefficient::GradientGridFunctionCoefficient (
const GridFunction *gf)
GridFunction *gf)
: VectorCoefficient((gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0)
{
GridFunc = gf;
}
void GradientGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
void GradientGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
{
GridFunc = gf; vdim = (gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0;
@@ -210,14 +227,14 @@ void GradientGridFunctionCoefficient::Eval(
}
CurlGridFunctionCoefficient::CurlGridFunctionCoefficient (
const GridFunction *gf)
GridFunction *gf)
: VectorCoefficient ((gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0)
{
GridFunc = gf;
}
void CurlGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
void CurlGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
{
GridFunc = gf; vdim = (gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0;
@@ -230,7 +247,7 @@ void CurlGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
}
DivergenceGridFunctionCoefficient::DivergenceGridFunctionCoefficient (
const GridFunction *gf) : Coefficient()
GridFunction *gf) : Coefficient()
{
GridFunc = gf;
}
@@ -758,61 +775,4 @@ double ComputeGlobalLpNorm(double p, VectorCoefficient &coeff, ParMesh &pmesh,
}
#endif
VectorQuadratureFunctionCoefficient::VectorQuadratureFunctionCoefficient(
QuadratureFunction &qf)
: VectorCoefficient(qf.GetVDim()), QuadF(qf), index(0) { }
void VectorQuadratureFunctionCoefficient::SetComponent(int _index, int _length)
{
MFEM_VERIFY(_index >= 0, "Index must be >= 0");
MFEM_VERIFY(_index < QuadF.GetVDim(),
"Index must be < QuadratureFunction length");
index = _index;
MFEM_VERIFY(_length > 0, "Length must be > 0");
MFEM_VERIFY(_length <= QuadF.GetVDim() - index,
"Length must be <= (QuadratureFunction length - index)");
vdim = _length;
}
void VectorQuadratureFunctionCoefficient::Eval(Vector &V,
ElementTransformation &T,
const IntegrationPoint &ip)
{
QuadF.HostRead();
if (index == 0 && vdim == QuadF.GetVDim())
{
QuadF.GetElementValues(T.ElementNo, ip.index, V);
}
else
{
Vector temp;
QuadF.GetElementValues(T.ElementNo, ip.index, temp);
V.SetSize(vdim);
for (int i = 0; i < vdim; i++)
{
V(i) = temp(index + i);
}
}
return;
}
QuadratureFunctionCoefficient::QuadratureFunctionCoefficient(
QuadratureFunction &qf) : QuadF(qf)
{
MFEM_VERIFY(qf.GetVDim() == 1, "QuadratureFunction's vdim must be 1");
}
double QuadratureFunctionCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
QuadF.HostRead();
Vector temp(1);
QuadF.GetElementValues(T.ElementNo, ip.index, temp);
return temp[0];
}
}
+20 -68
View File
@@ -164,18 +164,18 @@ class GridFunction;
class GridFunctionCoefficient : public Coefficient
{
private:
const GridFunction *GridF;
GridFunction *GridF;
int Component;
public:
GridFunctionCoefficient() : GridF(NULL), Component(1) { }
/** Construct GridFunctionCoefficient from a given GridFunction, and
optionally specify a component to use if it is a vector GridFunction. */
GridFunctionCoefficient (const GridFunction *gf, int comp = 1)
GridFunctionCoefficient (GridFunction *gf, int comp = 1)
{ GridF = gf; Component = comp; }
void SetGridFunction(const GridFunction *gf) { GridF = gf; }
const GridFunction * GetGridFunction() const { return GridF; }
void SetGridFunction(GridFunction *gf) { GridF = gf; }
GridFunction * GetGridFunction() const { return GridF; }
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
@@ -400,14 +400,14 @@ public:
class VectorGridFunctionCoefficient : public VectorCoefficient
{
protected:
const GridFunction *GridFunc;
GridFunction *GridFunc;
public:
VectorGridFunctionCoefficient() : VectorCoefficient(0), GridFunc(NULL) { }
VectorGridFunctionCoefficient(const GridFunction *gf);
VectorGridFunctionCoefficient(GridFunction *gf);
void SetGridFunction(const GridFunction *gf);
const GridFunction * GetGridFunction() const { return GridFunc; }
void SetGridFunction(GridFunction *gf);
GridFunction * GetGridFunction() const { return GridFunc; }
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -422,13 +422,13 @@ public:
class GradientGridFunctionCoefficient : public VectorCoefficient
{
protected:
const GridFunction *GridFunc;
GridFunction *GridFunc;
public:
GradientGridFunctionCoefficient(const GridFunction *gf);
GradientGridFunctionCoefficient(GridFunction *gf);
void SetGridFunction(const GridFunction *gf);
const GridFunction * GetGridFunction() const { return GridFunc; }
void SetGridFunction(GridFunction *gf);
GridFunction * GetGridFunction() const { return GridFunc; }
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -443,13 +443,13 @@ public:
class CurlGridFunctionCoefficient : public VectorCoefficient
{
protected:
const GridFunction *GridFunc;
GridFunction *GridFunc;
public:
CurlGridFunctionCoefficient(const GridFunction *gf);
CurlGridFunctionCoefficient(GridFunction *gf);
void SetGridFunction(const GridFunction *gf);
const GridFunction * GetGridFunction() const { return GridFunc; }
void SetGridFunction(GridFunction *gf);
GridFunction * GetGridFunction() const { return GridFunc; }
using VectorCoefficient::Eval;
virtual void Eval(Vector &V, ElementTransformation &T,
@@ -462,13 +462,13 @@ public:
class DivergenceGridFunctionCoefficient : public Coefficient
{
protected:
const GridFunction *GridFunc;
GridFunction *GridFunc;
public:
DivergenceGridFunctionCoefficient(const GridFunction *gf);
DivergenceGridFunctionCoefficient(GridFunction *gf);
void SetGridFunction(const GridFunction *gf) { GridFunc = gf; }
const GridFunction * GetGridFunction() const { return GridFunc; }
void SetGridFunction(GridFunction *gf) { GridFunc = gf; }
GridFunction * GetGridFunction() const { return GridFunc; }
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
@@ -946,54 +946,6 @@ public:
const IntegrationPoint &ip);
};
class QuadratureFunction;
/** @brief Vector quadrature function coefficient which requires that the
quadrature rules used for this vector coefficient be the same as those that
live within the supplied QuadratureFunction. */
class VectorQuadratureFunctionCoefficient : public VectorCoefficient
{
private:
const QuadratureFunction &QuadF; //do not own
int index;
public:
/// Constructor with a quadrature function as input
VectorQuadratureFunctionCoefficient(QuadratureFunction &qf);
/** Set the starting index within the QuadFunc that'll be used to
project outwards as well as the corresponding length. The projected length
should have the bounds of 1 <= length <= (length QuadFunc - index). */
void SetComponent(int _index, int _length);
const QuadratureFunction& GetQuadFunction() const { return QuadF; }
using VectorCoefficient::Eval;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~VectorQuadratureFunctionCoefficient() { }
};
/** @brief Quadrature function coefficient which requires that the quadrature
rules used for this coefficient be the same as those that live within the
supplied QuadratureFunction. */
class QuadratureFunctionCoefficient : public Coefficient
{
private:
const QuadratureFunction &QuadF;
public:
/// Constructor with a quadrature function as input
QuadratureFunctionCoefficient(QuadratureFunction &qf);
const QuadratureFunction& GetQuadFunction() const { return QuadF; }
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
virtual ~QuadratureFunctionCoefficient() { }
};
/** Compute the Lp norm of a function f.
\f$ \| f \|_{Lp} = ( \int_\Omega | f |^p d\Omega)^{1/p} \f$ */
double ComputeLpNorm(double p, Coefficient &coeff, Mesh &mesh,
+7 -1
View File
@@ -739,6 +739,12 @@ ParaViewDataCollection::ParaViewDataCollection(const std::string&
#endif
}
void ParaViewDataCollection::RegisterField(const std::string& field_name,
mfem::GridFunction *gf)
{
DataCollection::RegisterField(field_name,gf);
}
void ParaViewDataCollection::SetLevelsOfDetail(int levels_of_detail_)
{
levels_of_detail = levels_of_detail_;
@@ -809,7 +815,7 @@ void ParaViewDataCollection::Save()
// the directory is created
// create pvd file if needed
if (myid == 0 && !pvd_stream.is_open())
if (!pvd_stream.is_open())
{
std::string dpath=GenerateCollectionPath();
std::string pvdname=dpath+"/"+GeneratePVDFileName();
+4
View File
@@ -501,6 +501,10 @@ public:
ParaViewDataCollection(const std::string& collection_name,
mfem::Mesh *mesh_ = NULL);
/// Add a grid function to the collection
virtual void RegisterField(const std::string& field_name,
mfem::GridFunction *gf) override;
/// Set refinement levels - every element is uniformly split based on
/// levels_of_detail_
void SetLevelsOfDetail(int levels_of_detail_);
-73
View File
@@ -19,7 +19,6 @@ namespace mfem
ElementTransformation::ElementTransformation()
: IntPoint(static_cast<IntegrationPoint *>(NULL)),
EvalState(0),
geom(Geometry::INVALID),
Attribute(-1),
ElementNo(-1)
{ }
@@ -552,76 +551,4 @@ void IntegrationPointTransformation::Transform (const IntegrationRule &ir1,
}
}
void FaceElementTransformations::SetIntPoint(const IntegrationPoint *ip)
{
IsoparametricTransformation::SetIntPoint(ip);
if (Elem1)
{
Loc1.Transform(*ip, eip1);
Elem1->SetIntPoint(&eip1);
}
if (Elem2)
{
Loc2.Transform(*ip, eip2);
Elem2->SetIntPoint(&eip2);
}
}
ElementTransformation &
FaceElementTransformations::GetElement1Transformation()
{
MFEM_VERIFY(mask & 1 && Elem1 != NULL, "The ElementTransformation "
"for the element has not been configured for side 1.");
return *Elem1;
}
ElementTransformation &
FaceElementTransformations::GetElement2Transformation()
{
MFEM_VERIFY(mask & 2 && Elem2 != NULL, "The ElementTransformation "
"for the element has not been configured for side 2.");
return *Elem2;
}
IntegrationPointTransformation &
FaceElementTransformations::GetIntPoint1Transformation()
{
MFEM_VERIFY(mask & 4, "The IntegrationPointTransformation "
"for the element has not been configured for side 1.");
return Loc1;
}
IntegrationPointTransformation &
FaceElementTransformations::GetIntPoint2Transformation()
{
MFEM_VERIFY(mask & 8, "The IntegrationPointTransformation "
"for the element has not been configured for side 2.");
return Loc2;
}
void FaceElementTransformations::Transform(const IntegrationPoint &ip,
Vector &trans)
{
MFEM_VERIFY(mask & 16, "The ElementTransformation "
"for the face has not been configured.");
IsoparametricTransformation::Transform(ip, trans);
}
void FaceElementTransformations::Transform(const IntegrationRule &ir,
DenseMatrix &tr)
{
MFEM_VERIFY(mask & 16, "The ElementTransformation "
"for the face has not been configured.");
IsoparametricTransformation::Transform(ir, tr);
}
void FaceElementTransformations::Transform(const DenseMatrix &matrix,
DenseMatrix &result)
{
MFEM_VERIFY(mask & 16, "The ElementTransformation "
"for the face has not been configured.");
IsoparametricTransformation::Transform(matrix, result);
}
}
+4 -72
View File
@@ -48,29 +48,7 @@ protected:
const DenseMatrix &EvalInverseJ();
public:
/** This enumeration declares the values stored in
ElementTransformation::ElementType and indicates which group of objects
the index stored in ElementTransformation::ElementNo refers:
| ElementType | Range of ElementNo
+-------------+-------------------------
| ELEMENT | [0, Mesh::GetNE() )
| BDR_ELEMENT | [0, Mesh::GetNBE() )
| EDGE | [0, Mesh::GetNEdges() )
| FACE | [0, Mesh::GetNFaces() )
| BDR_FACE | [0, Mesh::GetNBE() )
*/
enum
{
ELEMENT = 1,
BDR_ELEMENT = 2,
EDGE = 3,
FACE = 4,
BDR_FACE = 5
};
int Attribute, ElementNo, ElementType;
int Attribute, ElementNo;
ElementTransformation();
@@ -378,58 +356,12 @@ public:
void Transform (const IntegrationRule &, IntegrationRule &);
};
class FaceElementTransformations : public IsoparametricTransformation
class FaceElementTransformations
{
private:
int mask;
IntegrationPoint eip1, eip2;
public:
int Elem1No, Elem2No;
Geometry::Type &FaceGeom; ///< @deprecated Use GetGeometryType instead
ElementTransformation *Elem1, *Elem2;
ElementTransformation *Face; ///< @deprecated No longer necessary
int Elem1No, Elem2No, FaceGeom;
ElementTransformation *Elem1, *Elem2, *Face;
IntegrationPointTransformation Loc1, Loc2;
FaceElementTransformations() : FaceGeom(geom), Face(this) {}
/** @brief Method to set the geometry type of the face.
@note This method is designed to be used when
[Par]Mesh::GetFaceTransformation will not be called i.e. when the face
transformation will not be needed but the neighboring element
transformations will be. Using this method to override the GeometryType
should only be done with great care.
*/
void SetGeometryType(Geometry::Type g) { geom = g; }
/// Set the mask indicating which portions of the object have been setup
/** The argument @a m is a bitmask used in
Mesh::GetFaceElementTransformations to indicate which portions of the
FaceElement Transformations object have been configured.
mask & 1: Elem1 is configured
mask & 2: Elem2 is configured
mask & 4: Loc1 is configured
mask & 8: Loc2 is configured
mask & 16: The Face transformation itself is configured
*/
void SetConfigurationMask(int m) { mask = m; }
int GetConfigurationMask() const { return mask; }
/** @brief Set the integration point in the Face and the two neighboring
elements, if present. */
void SetIntPoint(const IntegrationPoint *ip);
virtual void Transform(const IntegrationPoint &, Vector &);
virtual void Transform(const IntegrationRule &, DenseMatrix &);
virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result);
ElementTransformation & GetElement1Transformation();
ElementTransformation & GetElement2Transformation();
IntegrationPointTransformation & GetIntPoint1Transformation();
IntegrationPointTransformation & GetIntPoint2Transformation();
};
/* Elem1(Loc1(x)) = Face(x) = Elem2(Loc2(x))
-17
View File
@@ -50,21 +50,4 @@ void L2ZienkiewiczZhuEstimator::ComputeEstimates()
#endif // MFEM_USE_MPI
void LpErrorEstimator::ComputeEstimates()
{
MFEM_VERIFY(coef != NULL || vcoef != NULL,
"LpErrorEstimator has no coefficient! Call SetCoef first.");
error_estimates.SetSize(sol->FESpace()->GetMesh()->GetNE());
if (coef)
{
sol->ComputeElementLpErrors(local_norm_p, *coef, error_estimates);
}
else
{
sol->ComputeElementLpErrors(local_norm_p, *vcoef, error_estimates);
}
current_sequence = sol->FESpace()->GetMesh()->GetSequence();
}
} // namespace mfem
-82
View File
@@ -304,88 +304,6 @@ public:
#endif // MFEM_USE_MPI
/** @brief The LpErrorEstimator class compares the solution to a known
coefficient.
This class can be used, for example, to adapt a mesh to a non-trivial
initial condition in a time-dependent simulation. It can also be used to
force refinement in the neighborhood of small features before switching to a
more traditional error estimator.
The LpErrorEstimator supports either scalar or vector coefficients and works
both in serial and in parallel.
*/
class LpErrorEstimator : public ErrorEstimator
{
protected:
long current_sequence;
int local_norm_p;
Vector error_estimates;
Coefficient * coef;
VectorCoefficient * vcoef;
GridFunction * sol;
/// Check if the mesh of the solution was modified.
bool MeshIsModified()
{
long mesh_sequence = sol->FESpace()->GetMesh()->GetSequence();
MFEM_ASSERT(mesh_sequence >= current_sequence, "");
return (mesh_sequence > current_sequence);
}
/// Compute the element error estimates.
void ComputeEstimates();
public:
/** @brief Construct a new LpErrorEstimator object for a scalar field.
@param p Integer which selects which Lp norm to use.
@param sol The GridFunction representation of the scalar field.
Note: the coefficient must be set before use with the SetCoef method.
*/
LpErrorEstimator(int p, GridFunction &sol)
: current_sequence(-1), local_norm_p(p),
error_estimates(0), coef(NULL), vcoef(NULL), sol(&sol) { }
/** @brief Construct a new LpErrorEstimator object for a scalar field.
@param p Integer which selects which Lp norm to use.
@param coef The scalar Coefficient to compare to the solution.
@param sol The GridFunction representation of the scalar field.
*/
LpErrorEstimator(int p, Coefficient &coef, GridFunction &sol)
: current_sequence(-1), local_norm_p(p),
error_estimates(0), coef(&coef), vcoef(NULL), sol(&sol) { }
/** @brief Construct a new LpErrorEstimator object for a vector field.
@param p Integer which selects which Lp norm to use.
@param coef The vector VectorCoefficient to compare to the solution.
@param sol The GridFunction representation of the vector field.
*/
LpErrorEstimator(int p, VectorCoefficient &coef, GridFunction &sol)
: current_sequence(-1), local_norm_p(p),
error_estimates(0), coef(NULL), vcoef(&coef), sol(&sol) { }
/** @brief Set the exponent, p, of the Lp norm used for computing the local
element errors. */
void SetLocalErrorNormP(int p) { local_norm_p = p; }
void SetCoef(Coefficient &A) { coef = &A; }
void SetCoef(VectorCoefficient &A) { vcoef = &A; }
/// Reset the error estimator.
virtual void Reset() { current_sequence = -1; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors()
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
}
/// Destructor
virtual ~LpErrorEstimator() {}
};
} // namespace mfem
#endif // MFEM_ERROR_ESTIMATORS
+11 -20
View File
@@ -10287,12 +10287,12 @@ const double RT_QuadrilateralElement::nk[8] =
RT_QuadrilateralElement::RT_QuadrilateralElement(const int p,
const int cb_type,
const int ob_type)
: VectorTensorFiniteElement(2, 2*(p + 1)*(p + 2), p + 1, cb_type, ob_type,
H_DIV, DofMapType::L2_DOF_MAP),
dof2nk(Dof)
: VectorFiniteElement(2, Geometry::SQUARE, 2*(p + 1)*(p + 2), p + 1,
H_DIV, FunctionSpace::Qk),
cbasis1d(poly1d.GetBasis(p + 1, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type))),
dof_map(Dof), dof2nk(Dof)
{
dof_map.SetSize(Dof);
const double *cp = poly1d.ClosedPoints(p + 1, cb_type);
const double *op = poly1d.OpenPoints(p, ob_type);
const int dof2 = Dof/2;
@@ -10498,12 +10498,12 @@ const double RT_HexahedronElement::nk[18] =
RT_HexahedronElement::RT_HexahedronElement(const int p,
const int cb_type,
const int ob_type)
: VectorTensorFiniteElement(3, 3*(p + 1)*(p + 1)*(p + 2), p + 1, cb_type,
ob_type, H_DIV, DofMapType::L2_DOF_MAP),
dof2nk(Dof)
: VectorFiniteElement(3, Geometry::CUBE, 3*(p + 1)*(p + 1)*(p + 2), p + 1,
H_DIV, FunctionSpace::Qk),
cbasis1d(poly1d.GetBasis(p + 1, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type))),
dof_map(Dof), dof2nk(Dof)
{
dof_map.SetSize(Dof);
const double *cp = poly1d.ClosedPoints(p + 1, cb_type);
const double *op = poly1d.OpenPoints(p, ob_type);
const int dof3 = Dof/3;
@@ -11537,8 +11537,7 @@ const DofToQuad &VectorTensorFiniteElement::GetTensorDofToQuad(
{
MFEM_VERIFY(mode == DofToQuad::TENSOR, "invalid mode requested");
for (int i = 0;
i < (closed ? dof2quad_array.Size() : dof2quad_array_open.Size());
for (int i = 0; i < closed ? dof2quad_array.Size() : dof2quad_array_open.Size();
i++)
{
const DofToQuad &d2q = closed ? *dof2quad_array[i] : *dof2quad_array_open[i];
@@ -11591,14 +11590,6 @@ const DofToQuad &VectorTensorFiniteElement::GetTensorDofToQuad(
return *d2q;
}
VectorTensorFiniteElement::~VectorTensorFiniteElement()
{
for (int i = 0; i < dof2quad_array_open.Size(); i++)
{
delete dof2quad_array_open[i];
}
}
const double ND_QuadrilateralElement::tk[8] =
{ 1.,0., 0.,1., -1.,0., 0.,-1. };
+6 -6
View File
@@ -1930,8 +1930,6 @@ public:
const DofToQuad &GetTensorDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode,
const bool closed) const;
~VectorTensorFiniteElement();
};
class H1_SegmentElement : public NodalTensorFiniteElement
@@ -2432,16 +2430,17 @@ public:
};
class RT_QuadrilateralElement : public VectorTensorFiniteElement
class RT_QuadrilateralElement : public VectorFiniteElement
{
private:
static const double nk[8];
Poly_1D::Basis &cbasis1d, &obasis1d;
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_cx, shape_ox, shape_cy, shape_oy;
mutable Vector dshape_cx, dshape_cy;
#endif
Array<int> dof2nk;
Array<int> dof_map, dof2nk;
public:
RT_QuadrilateralElement(const int p,
@@ -2487,15 +2486,16 @@ public:
};
class RT_HexahedronElement : public VectorTensorFiniteElement
class RT_HexahedronElement : public VectorFiniteElement
{
static const double nk[18];
Poly_1D::Basis &cbasis1d, &obasis1d;
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_cx, shape_ox, shape_cy, shape_oy, shape_cz, shape_oz;
mutable Vector dshape_cx, dshape_cy, dshape_cz;
#endif
Array<int> dof2nk;
Array<int> dof_map, dof2nk;
public:
RT_HexahedronElement(const int p,
-55
View File
@@ -40,15 +40,6 @@ protected:
const int face_info);
public:
/** @brief Enumeration for ContType: defines the continuity of the field
across element interfaces.
*/
enum { CONTINUOUS, ///< Field is continuous across element interfaces
TANGENTIAL, ///< Tangential components of vector field
NORMAL, ///< Normal component of vector field
DISCONTINUOUS ///< Field is discontinuous across element interfaces
};
virtual const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const = 0;
@@ -61,8 +52,6 @@ public:
virtual const char * Name() const { return "Undefined"; }
virtual int GetContType() const = 0;
int HasFaceDofs(Geometry::Type GeomType) const;
virtual const FiniteElement *TraceFiniteElementForGeometry(
@@ -113,7 +102,6 @@ public:
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const;
virtual const char *Name() const { return h1_name; }
virtual int GetContType() const { return CONTINUOUS; }
FiniteElementCollection *GetTraceCollection() const;
int GetBasisType() const { return b_type; }
@@ -186,8 +174,6 @@ public:
int Or) const;
virtual const char *Name() const { return d_name; }
virtual int GetContType() const { return DISCONTINUOUS; }
virtual const FiniteElement *TraceFiniteElementForGeometry(
Geometry::Type GeomType) const
{
@@ -235,7 +221,6 @@ public:
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const;
virtual const char *Name() const { return rt_name; }
virtual int GetContType() const { return NORMAL; }
FiniteElementCollection *GetTraceCollection() const;
virtual ~RT_FECollection();
@@ -285,7 +270,6 @@ public:
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const;
virtual const char *Name() const { return nd_name; }
virtual int GetContType() const { return TANGENTIAL; }
FiniteElementCollection *GetTraceCollection() const;
virtual ~ND_FECollection();
@@ -350,8 +334,6 @@ public:
virtual const char *Name() const { return name; }
virtual int GetContType() const { return CONTINUOUS; }
FiniteElementCollection *GetTraceCollection() const;
virtual ~NURBSFECollection();
@@ -381,8 +363,6 @@ public:
int Or) const;
virtual const char * Name() const { return "Linear"; }
virtual int GetContType() const { return CONTINUOUS; }
};
/// Piecewise-(bi)quadratic continuous finite elements.
@@ -409,8 +389,6 @@ public:
int Or) const;
virtual const char * Name() const { return "Quadratic"; }
virtual int GetContType() const { return CONTINUOUS; }
};
/// Version of QuadraticFECollection with positive basis functions.
@@ -432,8 +410,6 @@ public:
int Or) const;
virtual const char * Name() const { return "QuadraticPos"; }
virtual int GetContType() const { return CONTINUOUS; }
};
/// Piecewise-(bi)cubic continuous finite elements.
@@ -461,8 +437,6 @@ public:
int Or) const;
virtual const char * Name() const { return "Cubic"; }
virtual int GetContType() const { return CONTINUOUS; }
};
/// Crouzeix-Raviart nonconforming elements in 2D.
@@ -484,8 +458,6 @@ public:
int Or) const;
virtual const char * Name() const { return "CrouzeixRaviart"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/// Piecewise-linear nonconforming finite elements in 3D.
@@ -509,8 +481,6 @@ public:
int Or) const;
virtual const char * Name() const { return "LinearNonConf3D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
@@ -534,8 +504,6 @@ public:
int Or) const;
virtual const char * Name() const { return "RT0_2D"; }
virtual int GetContType() const { return NORMAL; }
};
/** Second order Raviart-Thomas finite elements in 2D. This class is kept only
@@ -558,8 +526,6 @@ public:
int Or) const;
virtual const char * Name() const { return "RT1_2D"; }
virtual int GetContType() const { return NORMAL; }
};
/** Third order Raviart-Thomas finite elements in 2D. This class is kept only
@@ -582,8 +548,6 @@ public:
int Or) const;
virtual const char * Name() const { return "RT2_2D"; }
virtual int GetContType() const { return NORMAL; }
};
/** Piecewise-constant discontinuous finite elements in 2D. This class is kept
@@ -605,8 +569,6 @@ public:
int Or) const;
virtual const char * Name() const { return "Const2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/** Piecewise-linear discontinuous finite elements in 2D. This class is kept
@@ -629,8 +591,6 @@ public:
int Or) const;
virtual const char * Name() const { return "LinearDiscont2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/// Version of LinearDiscont2DFECollection with dofs in the Gaussian points.
@@ -653,8 +613,6 @@ public:
int Or) const;
virtual const char * Name() const { return "GaussLinearDiscont2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/// Linear (P1) finite elements on quadrilaterals.
@@ -670,7 +628,6 @@ public:
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const;
virtual const char * Name() const { return "P1OnQuad"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/** Piecewise-quadratic discontinuous finite elements in 2D. This class is kept
@@ -693,7 +650,6 @@ public:
int Or) const;
virtual const char * Name() const { return "QuadraticDiscont2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/// Version of QuadraticDiscont2DFECollection with positive basis functions.
@@ -711,7 +667,6 @@ public:
int Or) const
{ return NULL; }
virtual const char * Name() const { return "QuadraticPosDiscont2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/// Version of QuadraticDiscont2DFECollection with dofs in the Gaussian points.
@@ -734,7 +689,6 @@ public:
int Or) const;
virtual const char * Name() const { return "GaussQuadraticDiscont2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/** Piecewise-cubic discontinuous finite elements in 2D. This class is kept
@@ -757,7 +711,6 @@ public:
int Or) const;
virtual const char * Name() const { return "CubicDiscont2D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/** Piecewise-constant discontinuous finite elements in 3D. This class is kept
@@ -781,7 +734,6 @@ public:
int Or) const;
virtual const char * Name() const { return "Const3D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/** Piecewise-linear discontinuous finite elements in 3D. This class is kept
@@ -804,7 +756,6 @@ public:
int Or) const;
virtual const char * Name() const { return "LinearDiscont3D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/** Piecewise-quadratic discontinuous finite elements in 3D. This class is kept
@@ -827,7 +778,6 @@ public:
int Or) const;
virtual const char * Name() const { return "QuadraticDiscont3D"; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
/// Finite element collection on a macro-element.
@@ -853,7 +803,6 @@ public:
int Or) const;
virtual const char * Name() const { return "RefinedLinear"; }
virtual int GetContType() const { return CONTINUOUS; }
};
/** Lowest order Nedelec finite elements in 3D. This class is kept only for
@@ -876,7 +825,6 @@ public:
int Or) const;
virtual const char * Name() const { return "ND1_3D"; }
virtual int GetContType() const { return TANGENTIAL; }
};
/** First order Raviart-Thomas finite elements in 3D. This class is kept only
@@ -900,7 +848,6 @@ public:
int Or) const;
virtual const char * Name() const { return "RT0_3D"; }
virtual int GetContType() const { return NORMAL; }
};
/** Second order Raviart-Thomas finite elements in 3D. This class is kept only
@@ -923,7 +870,6 @@ public:
int Or) const;
virtual const char * Name() const { return "RT1_3D"; }
virtual int GetContType() const { return NORMAL; }
};
/// Discontinuous collection defined locally by a given finite element.
@@ -948,7 +894,6 @@ public:
virtual const char *Name() const { return d_name; }
virtual ~Local_FECollection() { delete Local_Element; }
virtual int GetContType() const { return DISCONTINUOUS; }
};
}
+1 -3
View File
@@ -2615,9 +2615,7 @@ const Operator &InterpolationGridTransfer::BackwardOperator()
L2ProjectionGridTransfer::L2Projection::L2Projection(
const FiniteElementSpace &fes_ho_, const FiniteElementSpace &fes_lor_)
: Operator(fes_lor_.GetVSize(), fes_ho_.GetVSize()),
fes_ho(fes_ho_),
fes_lor(fes_lor_)
: fes_ho(fes_ho_), fes_lor(fes_lor_)
{
Mesh *mesh_ho = fes_ho.GetMesh();
MFEM_VERIFY(mesh_ho->GetNumGeometries(mesh_ho->Dimension()) <= 1,
+1 -2
View File
@@ -906,8 +906,7 @@ protected:
const L2Projection &l2proj;
public:
L2Prolongation(const L2Projection &l2proj_)
: Operator(l2proj_.Width(), l2proj_.Height()), l2proj(l2proj_) { }
L2Prolongation(const L2Projection &l2proj_) : l2proj(l2proj_) { }
void Mult(const Vector &x, Vector &y) const
{
l2proj.Prolongate(x, y);
+29 -372
View File
@@ -236,6 +236,7 @@ void GridFunction::MakeTRef(FiniteElementSpace *f, Vector &tv, int tv_offset)
}
}
void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
GridFunction &flux,
Array<int>& count,
@@ -616,354 +617,17 @@ int GridFunction::GetFaceValues(int i, int side, const IntegrationRule &ir,
return dir;
}
void GridFunction::GetVectorValues(int i, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const
{
ElementTransformation *Tr = fes->GetElementTransformation(i);
Tr->Transform(ir, tr);
GetVectorValues(*Tr, ir, vals);
}
void be_to_bfe(Geometry::Type geom, int o, const IntegrationPoint &ip,
IntegrationPoint &fip)
{
if (geom == Geometry::TRIANGLE)
{
if (o == 2)
{
fip.x = 1.0 - ip.x - ip.y;
fip.y = ip.x;
}
else if (o == 4)
{
fip.x = ip.y;
fip.y = 1.0 - ip.x - ip.y;
}
else
{
fip.x = ip.x;
fip.y = ip.y;
}
fip.z = ip.z;
}
else
{
if (o == 2)
{
fip.x = ip.y;
fip.y = 1.0 - ip.x;
}
else if (o == 4)
{
fip.x = 1.0 - ip.x;
fip.y = 1.0 - ip.y;
}
else if (o == 6)
{
fip.x = 1.0 - ip.y;
fip.y = ip.x;
}
else
{
fip.x = ip.x;
fip.y = ip.y;
}
fip.z = ip.z;
}
fip.weight = ip.weight;
fip.index = ip.index;
}
double GridFunction::GetValue(ElementTransformation &T,
const IntegrationPoint &ip,
int comp, Vector *tr) const
{
if (tr)
{
T.SetIntPoint(&ip);
T.Transform(ip, *tr);
}
const FiniteElement * fe = NULL;
Array<int> dofs;
switch (T.ElementType)
{
case ElementTransformation::ELEMENT:
fe = fes->GetFE(T.ElementNo);
fes->GetElementDofs(T.ElementNo, dofs);
break;
case ElementTransformation::EDGE:
if (fes->FEColl()->GetContType() ==
FiniteElementCollection::CONTINUOUS)
{
fe = fes->GetEdgeElement(T.ElementNo);
fes->GetEdgeDofs(T.ElementNo, dofs);
}
else
{
MFEM_ABORT("GridFunction::GetValue: Field continuity type \""
<< fes->FEColl()->GetContType() << "\" not supported "
<< "on mesh edges.");
return NAN;
}
break;
case ElementTransformation::FACE:
if (fes->FEColl()->GetContType() ==
FiniteElementCollection::CONTINUOUS)
{
fe = fes->GetFaceElement(T.ElementNo);
fes->GetFaceDofs(T.ElementNo, dofs);
}
else
{
MFEM_ABORT("GridFunction::GetValue: Field continuity type \""
<< fes->FEColl()->GetContType() << "\" not supported "
<< "on mesh faces.");
return NAN;
}
break;
case ElementTransformation::BDR_ELEMENT:
{
if (fes->FEColl()->GetContType() ==
FiniteElementCollection::CONTINUOUS)
{
// This is a continuous field so we can evaluate it on the boundary.
fe = fes->GetBE(T.ElementNo);
fes->GetBdrElementDofs(T.ElementNo, dofs);
}
else
{
// This is a discontinuous field which cannot be evaluated on the
// boundary so we'll evaluate it in the neighboring element.
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
// Boundary elements and Boundary Faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, ip, fip);
FET->SetIntPoint(&fip);
ElementTransformation & T1 = FET->GetElement1Transformation();
return GetValue(T1, T1.GetIntPoint(), comp);
}
break;
}
case ElementTransformation::BDR_FACE:
{
FaceElementTransformations * FET =
dynamic_cast<FaceElementTransformations *>(&T);
// Evaluate in neighboring element for both continuous and
// discontinuous fields.
ElementTransformation & T1 = FET->GetElement1Transformation();
return GetValue(T1, T1.GetIntPoint(), comp);
}
default:
{
MFEM_ABORT("GridFunction::GetValue: Unsupported element type \""
<< T.ElementType << "\"");
return NAN;
}
}
fes->DofsToVDofs(comp-1, dofs);
Vector DofVal(dofs.Size()), LocVec;
if (fe->GetMapType() == FiniteElement::VALUE)
{
fe->CalcShape(ip, DofVal);
}
else
{
fe->CalcPhysShape(T, DofVal);
}
GetSubVector(dofs, LocVec);
return (DofVal * LocVec);
}
void GridFunction::GetValues(ElementTransformation &T,
const IntegrationRule &ir,
Vector &vals, int comp,
DenseMatrix *tr) const
{
if (tr)
{
T.Transform(ir, *tr);
}
int nip = ir.GetNPoints();
vals.SetSize(nip);
for (int j = 0; j < nip; j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T.SetIntPoint(&ip);
vals[j] = GetValue(T, ip, comp);
}
}
void GridFunction::GetVectorValue(ElementTransformation &T,
const IntegrationPoint &ip,
Vector &val, Vector *tr) const
{
if (tr)
{
T.SetIntPoint(&ip);
T.Transform(ip, *tr);
}
Array<int> vdofs;
const FiniteElement *fe = NULL;
switch (T.ElementType)
{
case ElementTransformation::ELEMENT:
fes->GetElementVDofs(T.ElementNo, vdofs);
fe = fes->GetFE(T.ElementNo);
break;
case ElementTransformation::EDGE:
if (fes->FEColl()->GetContType() ==
FiniteElementCollection::CONTINUOUS)
{
fe = fes->GetEdgeElement(T.ElementNo);
fes->GetEdgeVDofs(T.ElementNo, vdofs);
}
else
{
MFEM_ABORT("GridFunction::GetVectorValue: Field continuity type \""
<< fes->FEColl()->GetContType() << "\" not supported "
<< "on mesh edges.");
return;
}
break;
case ElementTransformation::FACE:
if (fes->FEColl()->GetContType() ==
FiniteElementCollection::CONTINUOUS)
{
fe = fes->GetFaceElement(T.ElementNo);
fes->GetFaceVDofs(T.ElementNo, vdofs);
}
else
{
MFEM_ABORT("GridFunction::GetVectorValue: Field continuity type \""
<< fes->FEColl()->GetContType() << "\" not supported "
<< "on mesh faces.");
return;
}
break;
case ElementTransformation::BDR_ELEMENT:
{
if (fes->FEColl()->GetContType() ==
FiniteElementCollection::CONTINUOUS)
{
// This is a continuous field so we can evaluate it on the boundary.
fes->GetBdrElementVDofs(T.ElementNo, vdofs);
fe = fes->GetBE(T.ElementNo);
}
else
{
// This is a discontinuous vector field which cannot be evaluated on
// the boundary so we'll evaluate it in the neighboring element.
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
// Boundary elements and Boundary Faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, ip, fip);
FET->SetIntPoint(&fip);
ElementTransformation & T1 = FET->GetElement1Transformation();
return GetVectorValue(T1, T1.GetIntPoint(), val);
}
break;
}
case ElementTransformation::BDR_FACE:
{
FaceElementTransformations * FET =
dynamic_cast<FaceElementTransformations *>(&T);
// Evaluate in neighboring element for both continuous and
// discontinuous fields.
ElementTransformation & T1 = FET->GetElement1Transformation();
return GetVectorValue(T1, T1.GetIntPoint(), val);
}
default:
{
MFEM_ABORT("GridFunction::GetVectorValue: Unsupported element type \""
<< T.ElementType << "\"");
if (val.Size() > 0) { val = NAN; }
return;
}
}
int dof = fe->GetDof();
Vector loc_data;
GetSubVector(vdofs, loc_data);
if (fe->GetRangeType() == FiniteElement::SCALAR)
{
Vector shape(dof);
if (fe->GetMapType() == FiniteElement::VALUE)
{
fe->CalcShape(ip, shape);
}
else
{
fe->CalcPhysShape(T, shape);
}
int vdim = fes->GetVDim();
val.SetSize(vdim);
for (int k = 0; k < vdim; k++)
{
val(k) = shape * ((const double *)loc_data + dof * k);
}
}
else
{
int spaceDim = fes->GetMesh()->SpaceDimension();
DenseMatrix vshape(dof, spaceDim);
fe->CalcVShape(T, vshape);
val.SetSize(spaceDim);
vshape.MultTranspose(loc_data, val);
}
}
void GridFunction::GetVectorValues(ElementTransformation &T,
const IntegrationRule &ir,
DenseMatrix &vals,
DenseMatrix *tr) const
DenseMatrix &vals) const
{
if (tr)
{
T.Transform(ir, *tr);
}
const FiniteElement *FElem = fes->GetFE(T.ElementNo);
int dof = FElem->GetDof();
Array<int> vdofs;
fes->GetElementVDofs(T.ElementNo, vdofs);
Vector loc_data;
GetSubVector(vdofs, loc_data);
int nip = ir.GetNPoints();
if (FElem->GetRangeType() == FiniteElement::SCALAR)
{
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
@@ -975,7 +639,6 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
{
const IntegrationPoint &ip = ir.IntPoint(j);
FElem->CalcShape(ip, shape);
for (int k = 0; k < vdim; k++)
{
vals(k,j) = shape * ((const double *)loc_data + dof * k);
@@ -986,22 +649,28 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
{
int spaceDim = fes->GetMesh()->SpaceDimension();
DenseMatrix vshape(dof, spaceDim);
vals.SetSize(spaceDim, nip);
Vector val_j;
for (int j = 0; j < nip; j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T.SetIntPoint(&ip);
FElem->CalcVShape(T, vshape);
vals.GetColumnReference(j, val_j);
vshape.MultTranspose(loc_data, val_j);
}
}
}
void GridFunction::GetVectorValues(int i, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const
{
ElementTransformation *Tr = fes->GetElementTransformation(i);
Tr->Transform(ir, tr);
GetVectorValues(*Tr, ir, vals);
}
int GridFunction::GetFaceVectorValues(
int i, int side, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const
@@ -1033,13 +702,13 @@ int GridFunction::GetFaceVectorValues(
{
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 4);
Transf->Loc1.Transform(ir, eir);
GetVectorValues(*Transf->Elem1, eir, vals, &tr);
GetVectorValues(Transf->Elem1No, eir, vals, tr);
}
else
{
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 8);
Transf->Loc2.Transform(ir, eir);
GetVectorValues(*Transf->Elem2, eir, vals, &tr);
GetVectorValues(Transf->Elem2No, eir, vals, tr);
}
return di;
@@ -2047,8 +1716,6 @@ void GridFunction::ProjectCoefficient(
ElementTransformation *T = NULL;
const FiniteElement *fe = NULL;
fes->BuildDofToArrays(); // ensures GetElementForDof(), GetLocalDofForDof() initialized.
for (int i = 0; i < dofs.Size(); i++)
{
int dof = dofs[i], j = fes->GetElementForDof(dof);
@@ -2090,8 +1757,6 @@ void GridFunction::ProjectCoefficient(
Vector val;
fes->BuildDofToArrays(); // ensures GetElementForDof(), GetLocalDofForDof() initialized.
for (int i = 0; i < dofs.Size(); i++)
{
int dof = dofs[i], j = fes->GetElementForDof(dof);
@@ -2344,7 +2009,7 @@ double GridFunction::ComputeL2Error(
fdof = fe->GetDof();
transf = fes->GetElementTransformation(i);
shape.SetSize(fdof);
intorder = 2*fe->GetOrder() + 3; // <----------
intorder = 2*fe->GetOrder() + 1; // <----------
const IntegrationRule *ir;
if (irs)
{
@@ -2399,7 +2064,7 @@ double GridFunction::ComputeL2Error(
{
if (elems != NULL && (*elems)[i] == 0) { continue; }
fe = fes->GetFE(i);
int intorder = 2*fe->GetOrder() + 3; // <----------
int intorder = 2*fe->GetOrder() + 1; // <----------
const IntegrationRule *ir;
if (irs)
{
@@ -2503,7 +2168,7 @@ double GridFunction::ComputeH1Error(
}
intorder = 2 * intorder; // <-------------
const IntegrationRule &ir =
IntRules.Get(face_elem_transf->GetGeometryType(), intorder);
IntRules.Get(face_elem_transf->FaceGeom, intorder);
err_val.SetSize(ir.GetNPoints());
ell_coeff_val.SetSize(ir.GetNPoints());
// side 1
@@ -2560,7 +2225,7 @@ double GridFunction::ComputeH1Error(
}
}
face_elem_transf = mesh->GetFaceElementTransformations(i, 16);
transf = face_elem_transf;
transf = face_elem_transf->Face;
for (j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
@@ -2594,7 +2259,7 @@ double GridFunction::ComputeMaxError(
fdof = fe->GetDof();
transf = fes->GetElementTransformation(i);
shape.SetSize(fdof);
intorder = 2*fe->GetOrder() + 3; // <----------
intorder = 2*fe->GetOrder() + 1; // <----------
const IntegrationRule *ir;
if (irs)
{
@@ -2760,7 +2425,7 @@ double GridFunction::ComputeLpError(const double p, Coefficient &exsol,
}
else
{
int intorder = 2*fe->GetOrder() + 3; // <----------
int intorder = 2*fe->GetOrder() + 1; // <----------
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
}
GetValues(i, *ir, vals);
@@ -2807,13 +2472,10 @@ double GridFunction::ComputeLpError(const double p, Coefficient &exsol,
}
void GridFunction::ComputeElementLpErrors(const double p, Coefficient &exsol,
Vector &error,
GridFunction &error,
Coefficient *weight,
const IntegrationRule *irs[]) const
{
MFEM_ASSERT(error.Size() == fes->GetNE(),
"Incorrect size for result vector");
error = 0.0;
const FiniteElement *fe;
ElementTransformation *T;
@@ -2829,7 +2491,7 @@ void GridFunction::ComputeElementLpErrors(const double p, Coefficient &exsol,
}
else
{
int intorder = 2*fe->GetOrder() + 3; // <----------
int intorder = 2*fe->GetOrder() + 1; // <----------
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
}
GetValues(i, *ir, vals);
@@ -2893,7 +2555,7 @@ double GridFunction::ComputeLpError(const double p, VectorCoefficient &exsol,
}
else
{
int intorder = 2*fe->GetOrder() + 3; // <----------
int intorder = 2*fe->GetOrder() + 1; // <----------
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
}
T = fes->GetElementTransformation(i);
@@ -2965,14 +2627,11 @@ double GridFunction::ComputeLpError(const double p, VectorCoefficient &exsol,
void GridFunction::ComputeElementLpErrors(const double p,
VectorCoefficient &exsol,
Vector &error,
GridFunction &error,
Coefficient *weight,
VectorCoefficient *v_weight,
const IntegrationRule *irs[]) const
{
MFEM_ASSERT(error.Size() == fes->GetNE(),
"Incorrect size for result vector");
error = 0.0;
const FiniteElement *fe;
ElementTransformation *T;
@@ -2989,7 +2648,7 @@ void GridFunction::ComputeElementLpErrors(const double p,
}
else
{
int intorder = 2*fe->GetOrder() + 3; // <----------
int intorder = 2*fe->GetOrder() + 1; // <----------
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
}
T = fes->GetElementTransformation(i);
@@ -2999,15 +2658,15 @@ void GridFunction::ComputeElementLpErrors(const double p,
loc_errs.SetSize(vals.Width());
if (!v_weight)
{
// compute the lengths of the errors at the integration points thus the
// vector norm is rotationally invariant
// compute the lengths of the errors at the integration points
// thus the vector norm is rotationally invariant
vals.Norm2(loc_errs);
}
else
{
v_weight->Eval(exact_vals, *T, *ir);
// column-wise dot product of the vector error (in vals) and the vector
// weight (in exact_vals)
// column-wise dot product of the vector error (in vals) and the
// vector weight (in exact_vals)
for (int j = 0; j < vals.Width(); j++)
{
double err = 0.0;
@@ -3139,9 +2798,7 @@ void GridFunction::SaveVTK(std::ostream &out, const std::string &field_name,
RefG = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(i), ref, 1);
// GetVectorValues(i, RefG->RefPts, vval, pmat);
ElementTransformation * T = mesh->GetElementTransformation(i);
GetVectorValues(*T, RefG->RefPts, vval, &pmat);
GetVectorValues(i, RefG->RefPts, vval, pmat);
for (int j = 0; j < vval.Width(); j++)
{
+26 -157
View File
@@ -144,133 +144,17 @@ public:
/// Returns the values in the vertices of i'th element for dimension vdim.
void GetNodalValues(int i, Array<double> &nval, int vdim = 1) const;
/** @name Element index Get Value Methods
These methods take an element index and return the interpolated value of
the field at a given reference point within the element.
@warning These methods retrieve and use the ElementTransformation object
from the mfem::Mesh. This can alter the state of the element
transformation object and can also lead to unexpected results when the
ElementTransformation object is already in use such as when these methods
are called from within an integration loop. Consider using
GetValue(ElementTransformation &T, ...) instead.
*/
///@{
/** Return a scalar value from within the given element. */
virtual double GetValue(int i, const IntegrationPoint &ip,
int vdim = 1) const;
/** Return a vector value from within the given element. */
void GetVectorValue(int i, const IntegrationPoint &ip, Vector &val) const;
///@}
/** @name Element Index Get Values Methods
These are convenience methods for repeatedly calling GetValue for
multiple points within a given element. The GetValues methods are
optimized and should perform better than repeatedly calling GetValue. The
GetVectorValues method simply calls GetVectorValue repeatedly.
@warning These methods retrieve and use the ElementTransformation object
from the mfem::Mesh. This can alter the state of the element
transformation object and can also lead to unexpected results when the
ElementTransformation object is already in use such as when these methods
are called from within an integration loop. Consider using
GetValues(ElementTransformation &T, ...) instead.
*/
///@{
/** Compute a collection of scalar values from within the element indicated
by the index i. */
void GetValues(int i, const IntegrationRule &ir, Vector &vals,
int vdim = 1) const;
/** Compute a collection of vector values from within the element indicated
by the index i. */
void GetValues(int i, const IntegrationRule &ir, Vector &vals,
DenseMatrix &tr, int vdim = 1) const;
void GetVectorValues(int i, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const;
///@}
/** @name ElementTransformation Get Value Methods
These member functions are designed for use within
GridFunctionCoefficient objects. These can be used with
ElementTransformation objects coming from either
Mesh::GetElementTransformation() or Mesh::GetBdrElementTransformation().
@note These methods do not reset the ElementTransformation object so they
should be safe to use within integration loops or other contexts where
the ElementTransformation is already in use.
*/
///@{
/** Return a scalar value from within the element indicated by the
ElementTransformation Object. */
double GetValue(ElementTransformation &T, const IntegrationPoint &ip,
int comp = 0, Vector *tr = NULL) const;
/** Return a vector value from within the element indicated by the
ElementTransformation Object. */
void GetVectorValue(ElementTransformation &T, const IntegrationPoint &ip,
Vector &val, Vector *tr = NULL) const;
///@}
/** @name ElementTransformation Get Values Methods
These are convenience methods for repeatedly calling GetValue for
multiple points within a given element. They work by calling either the
ElementTransformation or FaceElementTransformations versions described
above. Consequently, these methods should not be expected to run faster
than calling the above methods in an external loop.
@note These methods do not reset the ElementTransformation object so they
should be safe to use within integration loops or other contexts where
the ElementTransformation is already in use.
@note These methods can also be used with FaceElementTransformations
objects.
*/
///@{
/** Compute a collection of scalar values from within the element indicated
by the ElementTransformation object. */
void GetValues(ElementTransformation &T, const IntegrationRule &ir,
Vector &vals, int comp = 0, DenseMatrix *tr = NULL) const;
/** Compute a collection of vector values from within the element indicated
by the ElementTransformation object. */
void GetVectorValues(ElementTransformation &T, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix *tr = NULL) const;
///@}
/** @name Face Index Get Values Methods
These methods are designed to work with Discontinuous Galerkin basis
functions. They compute field values on the interface between elements,
or on boundary elements, by interpolating the field in a neighboring
element. The \a side argument indices which neighboring element should be
used: 0, 1, or 2 (automatically chosen).
@warning These methods retrieve and use the FaceElementTransformations
object from the mfem::Mesh. This can alter the state of the face element
transformations object and can also lead to unexpected results when the
FaceElementTransformations object is already in use such as when these
methods are called from within an integration loop. Consider using
GetValues(ElementTransformation &T, ...) instead.
*/
///@{
/** Compute a collection of scalar values from within the face
indicated by the index i. */
int GetFaceValues(int i, int side, const IntegrationRule &ir, Vector &vals,
DenseMatrix &tr, int vdim = 1) const;
/** Compute a collection of vector values from within the face
indicated by the index i. */
int GetFaceVectorValues(int i, int side, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const;
///@}
void GetLaplacians(int i, const IntegrationRule &ir, Vector &laps,
int vdim = 1) const;
@@ -283,6 +167,18 @@ public:
void GetHessians(int i, const IntegrationRule &ir, DenseMatrix &hess,
DenseMatrix &tr, int vdim = 1) const;
int GetFaceValues(int i, int side, const IntegrationRule &ir, Vector &vals,
DenseMatrix &tr, int vdim = 1) const;
void GetVectorValues(ElementTransformation &T, const IntegrationRule &ir,
DenseMatrix &vals) const;
void GetVectorValues(int i, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const;
int GetFaceVectorValues(int i, int side, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const;
void GetValuesFrom(const GridFunction &orig_func);
void GetBdrValuesFrom(const GridFunction &orig_func);
@@ -340,10 +236,12 @@ public:
virtual void ProjectCoefficient(Coefficient &coeff);
// call fes -> BuildDofToArrays() before using this projection
void ProjectCoefficient(Coefficient &coeff, Array<int> &dofs, int vd = 0);
void ProjectCoefficient(VectorCoefficient &vcoeff);
// call fes -> BuildDofToArrays() before using this projection
void ProjectCoefficient(VectorCoefficient &vcoeff, Array<int> &dofs);
void ProjectCoefficient(Coefficient *coeff[]);
@@ -467,28 +365,28 @@ public:
const IntegrationRule *irs[] = NULL) const;
/** Compute the Lp error in each element of the mesh and store the results in
the Vector @a error. The result should be of length number of elements,
for example an L2 GridFunction of order zero using map type VALUE. */
the GridFunction @a error. The result should be an L2 GridFunction of
order zero using map type VALUE. */
virtual void ComputeElementLpErrors(const double p, Coefficient &exsol,
Vector &error,
GridFunction &error,
Coefficient *weight = NULL,
const IntegrationRule *irs[] = NULL
) const;
virtual void ComputeElementL1Errors(Coefficient &exsol,
Vector &error,
GridFunction &error,
const IntegrationRule *irs[] = NULL
) const
{ ComputeElementLpErrors(1.0, exsol, error, NULL, irs); }
virtual void ComputeElementL2Errors(Coefficient &exsol,
Vector &error,
GridFunction &error,
const IntegrationRule *irs[] = NULL
) const
{ ComputeElementLpErrors(2.0, exsol, error, NULL, irs); }
virtual void ComputeElementMaxErrors(Coefficient &exsol,
Vector &error,
GridFunction &error,
const IntegrationRule *irs[] = NULL
) const
{ ComputeElementLpErrors(infinity(), exsol, error, NULL, irs); }
@@ -502,29 +400,29 @@ public:
const IntegrationRule *irs[] = NULL) const;
/** Compute the Lp error in each element of the mesh and store the results in
the Vector @ error. The result should be of length number of elements,
for example an L2 GridFunction of order zero using map type VALUE. */
the GridFunction @ error. The result should be an L2 GridFunction of
order zero using map type VALUE. */
virtual void ComputeElementLpErrors(const double p, VectorCoefficient &exsol,
Vector &error,
GridFunction &error,
Coefficient *weight = NULL,
VectorCoefficient *v_weight = NULL,
const IntegrationRule *irs[] = NULL
) const;
virtual void ComputeElementL1Errors(VectorCoefficient &exsol,
Vector &error,
GridFunction &error,
const IntegrationRule *irs[] = NULL
) const
{ ComputeElementLpErrors(1.0, exsol, error, NULL, NULL, irs); }
virtual void ComputeElementL2Errors(VectorCoefficient &exsol,
Vector &error,
GridFunction &error,
const IntegrationRule *irs[] = NULL
) const
{ ComputeElementLpErrors(2.0, exsol, error, NULL, NULL, irs); }
virtual void ComputeElementMaxErrors(VectorCoefficient &exsol,
Vector &error,
GridFunction &error,
const IntegrationRule *irs[] = NULL
) const
{ ComputeElementLpErrors(infinity(), exsol, error, NULL, NULL, irs); }
@@ -735,16 +633,6 @@ public:
*/
inline void GetElementValues(int idx, Vector &values) const;
/// Return the quadrature function values at an integration point.
/** The result is stored in the Vector @a values as a reference to the
global values. */
inline void GetElementValues(int idx, const int ip_num, Vector &values);
/// Return the quadrature function values at an integration point.
/** The result is stored in the Vector @a values as a copy to the
global values. */
inline void GetElementValues(int idx, const int ip_num, Vector &values) const;
/// Return all values associated with mesh element @a idx in a DenseMatrix.
/** The result is stored in the DenseMatrix @a values as a reference to the
global values.
@@ -849,25 +737,6 @@ inline void QuadratureFunction::GetElementValues(int idx, Vector &values) const
}
}
inline void QuadratureFunction::GetElementValues(int idx, const int ip_num,
Vector &values)
{
const int s_offset = qspace->element_offsets[idx] * vdim + ip_num * vdim;
values.NewDataAndSize(data + s_offset, vdim);
}
inline void QuadratureFunction::GetElementValues(int idx, const int ip_num,
Vector &values) const
{
const int s_offset = qspace->element_offsets[idx] * vdim + ip_num * vdim;
values.SetSize(vdim);
const double *q = data + s_offset;
for (int i = 0; i < values.Size(); i++)
{
values(i) = *(q++);
}
}
inline void QuadratureFunction::GetElementValues(int idx, DenseMatrix &values)
{
const int s_offset = qspace->element_offsets[idx];
+31 -99
View File
@@ -29,14 +29,12 @@ namespace mfem
{
FindPointsGSLIB::FindPointsGSLIB()
: mesh(NULL), ir_simplex(NULL), fdata2D(NULL), fdata3D(NULL),
dim(-1), gsl_mesh(), gsl_ref(), gsl_dist(), setupflag(false)
: mesh(NULL), ir_simplex(NULL), gsl_mesh(), fdata2D(NULL), fdata3D(NULL),
dim(-1)
{
gsl_comm = new comm;
#ifdef MFEM_USE_MPI
int initialized;
MPI_Initialized(&initialized);
if (!initialized) { MPI_Init(NULL, NULL); }
MPI_Init(NULL, NULL);
MPI_Comm comm = MPI_COMM_WORLD;;
comm_init(gsl_comm, comm);
#else
@@ -52,29 +50,28 @@ FindPointsGSLIB::~FindPointsGSLIB()
#ifdef MFEM_USE_MPI
FindPointsGSLIB::FindPointsGSLIB(MPI_Comm _comm)
: mesh(NULL), ir_simplex(NULL), fdata2D(NULL), fdata3D(NULL),
dim(-1), gsl_mesh(), gsl_ref(), gsl_dist(), setupflag(false)
: mesh(NULL), ir_simplex(NULL), gsl_mesh(), fdata2D(NULL), fdata3D(NULL),
dim(-1)
{
gsl_comm = new comm;
comm_init(gsl_comm, _comm);
}
#endif
void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
const int npt_max)
void FindPointsGSLIB::Setup(Mesh &m, double bb_t, double newt_tol, int npt_max)
{
MFEM_VERIFY(m.GetNodes() != NULL, "Mesh nodes are required.");
MFEM_VERIFY(m.GetNumGeometries(m.Dimension()) == 1,
"Mixed meshes are not currently supported in FindPointsGSLIB.");
// call FreeData if FindPointsGSLIB::Setup has been called already
if (setupflag) { FreeData(); }
mesh = &m;
dim = mesh->Dimension();
const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(0);
unsigned dof1D = fe->GetOrder() + 1;
const int gt = fe->GetGeomType();
int NE = mesh->GetNE(),
dof_cnt = fe->GetDof(),
pts_cnt = NE * dof_cnt,
gt = fe->GetGeomType();
if (gt == Geometry::TRIANGLE || gt == Geometry::TETRAHEDRON ||
gt == Geometry::PRISM)
@@ -90,8 +87,8 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
MFEM_ABORT("Element type not currently supported in FindPointsGSLIB.");
}
const int pts_cnt = gsl_mesh.Size()/dim,
NEtot = pts_cnt/(int)pow(dof1D, dim);
pts_cnt = gsl_mesh.Size()/dim;
int NEtot = pts_cnt/(int)pow(dof1D, dim);
if (dim == 2)
{
@@ -110,7 +107,6 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
fdata3D = findpts_setup_3(gsl_comm, elx, nr, NEtot, mr, bb_t,
pts_cnt, pts_cnt, npt_max, newt_tol);
}
setupflag = true;
}
void FindPointsGSLIB::FindPoints(const Vector &point_pos,
@@ -119,7 +115,6 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
Array<unsigned int> &elem_ids,
Vector &ref_pos, Vector &dist)
{
MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
const int points_cnt = point_pos.Size() / dim;
if (dim == 2)
{
@@ -155,90 +150,34 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
}
}
void FindPointsGSLIB::FindPoints(const Vector &point_pos)
{
const int points_cnt = point_pos.Size() / dim;
gsl_code.SetSize(points_cnt);
gsl_proc.SetSize(points_cnt);
gsl_elem.SetSize(points_cnt);
gsl_ref.SetSize(points_cnt * dim);
gsl_dist.SetSize(points_cnt);
FindPoints(point_pos, gsl_code, gsl_proc, gsl_elem, gsl_ref, gsl_dist);
}
void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
const double bb_t, const double newt_tol,
const int npt_max)
{
if (!setupflag || (mesh != &m) )
{
Setup(m, bb_t, newt_tol, npt_max);
}
FindPoints(point_pos);
}
void FindPointsGSLIB::Interpolate(Array<unsigned int> &codes,
Array<unsigned int> &proc_ids,
Array<unsigned int> &elem_ids,
Vector &ref_pos, const GridFunction &field_in,
Vector &field_out)
{
FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
GridFunction field_in_scalar(&ind_fes);
Vector node_vals;
GetNodeValues(field_in, node_vals);
const int ncomp = field_in.FESpace()->GetVDim(),
points_fld = field_in.Size() / ncomp,
points_cnt = codes.Size();
for (int i = 0; i < ncomp; i++)
const int points_cnt = ref_pos.Size() / dim;
if (dim==2)
{
const int dataptrin = i*points_fld,
dataptrout = i*points_cnt;
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin, points_fld);
GetNodeValues(field_in_scalar, node_vals);
if (dim==2)
{
findpts_eval_2(field_out.GetData()+dataptrout, sizeof(double),
codes.GetData(), sizeof(unsigned int),
proc_ids.GetData(), sizeof(unsigned int),
elem_ids.GetData(), sizeof(unsigned int),
ref_pos.GetData(), sizeof(double) * dim,
points_cnt, node_vals.GetData(), fdata2D);
}
else
{
findpts_eval_3(field_out.GetData()+dataptrout, sizeof(double),
codes.GetData(), sizeof(unsigned int),
proc_ids.GetData(), sizeof(unsigned int),
elem_ids.GetData(), sizeof(unsigned int),
ref_pos.GetData(), sizeof(double) * dim,
points_cnt, node_vals.GetData(), fdata3D);
}
findpts_eval_2(field_out.GetData(), sizeof(double),
codes.GetData(), sizeof(unsigned int),
proc_ids.GetData(), sizeof(unsigned int),
elem_ids.GetData(), sizeof(unsigned int),
ref_pos.GetData(), sizeof(double) * dim,
points_cnt, node_vals.GetData(), fdata2D);
}
else
{
findpts_eval_3(field_out.GetData(), sizeof(double),
codes.GetData(), sizeof(unsigned int),
proc_ids.GetData(), sizeof(unsigned int),
elem_ids.GetData(), sizeof(unsigned int),
ref_pos.GetData(), sizeof(double) * dim,
points_cnt, node_vals.GetData(), fdata3D);
}
}
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
Vector &field_out)
{
Interpolate(gsl_code, gsl_proc, gsl_elem, gsl_ref, field_in, field_out);
}
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
const GridFunction &field_in, Vector &field_out)
{
FindPoints(point_pos);
Interpolate(gsl_code, gsl_proc, gsl_elem, gsl_ref, field_in, field_out);
}
void FindPointsGSLIB::Interpolate(Mesh &m, const Vector &point_pos,
const GridFunction &field_in, Vector &field_out)
{
FindPoints(m, point_pos);
Interpolate(gsl_code, gsl_proc, gsl_elem, gsl_ref, field_in, field_out);
}
void FindPointsGSLIB::FreeData()
@@ -251,13 +190,7 @@ void FindPointsGSLIB::FreeData()
{
findpts_free_3(fdata3D);
}
setupflag = false;
gsl_code.DeleteAll();
gsl_proc.DeleteAll();
gsl_elem.DeleteAll();
gsl_mesh.Destroy();
gsl_ref.Destroy();
gsl_dist.Destroy();
}
void FindPointsGSLIB::GetNodeValues(const GridFunction &gf_in,
@@ -359,7 +292,7 @@ void FindPointsGSLIB::GetSimplexNodalCoordinates()
const GridFunction *nodes = mesh->GetNodes();
Mesh *meshsplit = NULL;
const int NE = mesh->GetNE();
int NEsplit = -1;
int NEsplit;
// Split the reference element into a reference submesh of quads or hexes.
if (gt == Geometry::TRIANGLE)
@@ -453,7 +386,6 @@ void FindPointsGSLIB::GetSimplexNodalCoordinates()
}
meshsplit->FinalizeHexMesh(1, 1, true);
}
else { MFEM_ABORT("Unsupported geometry type."); }
// Curve the reference submesh.
H1_FECollection fec(fe->GetOrder(), dim);
+3 -30
View File
@@ -29,12 +29,10 @@ class FindPointsGSLIB
protected:
Mesh *mesh;
IntegrationRule *ir_simplex;
Vector gsl_mesh;
struct findpts_data_2 *fdata2D;
struct findpts_data_3 *fdata3D;
int dim;
Array<unsigned int> gsl_code, gsl_proc, gsl_elem;
Vector gsl_mesh, gsl_ref, gsl_dist;
bool setupflag;
struct comm *gsl_comm;
@@ -61,8 +59,7 @@ public:
@param[in] newt_tol Newton tolerance for the gslib search methods.
@param[in] npt_max Number of points for simultaneous iteration. This
alters performance and memory footprint. */
void Setup(Mesh &m, const double bb_t = 0.1, const double newt_tol = 1.0e-12,
const int npt_max = 256);
void Setup(Mesh &m, double bb_t, double newt_tol, int npt_max);
/** Searches positions given in physical space by @a point_pos. All output
Arrays and Vectors are expected to have the correct size.
@@ -76,15 +73,11 @@ public:
@param[out] ref_pos Reference coordinates of the found point. Ordered
by vdim (XYZ,XYZ,XYZ...).
Note: the gslib reference frame is [-1,1].
@param[out] dist Distance between the sought and the found point
@param[out] dist Distance between the seeked and the found point
in physical space. */
void FindPoints(const Vector &point_pos, Array<unsigned int> &codes,
Array<unsigned int> &proc_ids, Array<unsigned int> &elem_ids,
Vector &ref_pos, Vector &dist);
void FindPoints(const Vector &point_pos);
/// Setup FindPoints and search positions
void FindPoints(Mesh &m, const Vector &point_pos, const double bb_t = 0.1,
const double newt_tol = 1.0e-12, const int npt_max = 256);
/** Interpolation of field values at prescribed reference space positions.
@@ -103,31 +96,11 @@ public:
void Interpolate(Array<unsigned int> &codes, Array<unsigned int> &proc_ids,
Array<unsigned int> &elem_ids, Vector &ref_pos,
const GridFunction &field_in, Vector &field_out);
void Interpolate(const GridFunction &field_in, Vector &field_out);
/** Search positions and interpolate */
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
Vector &field_out);
/** Setup FindPoints, search positions and interpolate */
void Interpolate(Mesh &m, const Vector &point_pos,
const GridFunction &field_in, Vector &field_out);
/** Cleans up memory allocated internally by gslib.
Note that in parallel, this must be called before MPI_Finalize(), as
it calls MPI_Comm_free() for internal gslib communicators. */
void FreeData();
/// Return code for each point searched by FindPoints: inside element (0), on
/// element boundary (1), or not found (2).
const Array<unsigned int> &GetCode() const { return gsl_code; }
/// Return element number for each point found by FindPoints.
const Array<unsigned int> &GetElem() const { return gsl_elem; }
/// Return MPI rank on which each point was found by FindPoints.
const Array<unsigned int> &GetProc() const { return gsl_proc; }
/// Return reference coordinates for each point found by FindPoints.
const Vector &GetReferencePosition() const { return gsl_ref; }
/// Return distance Distance between the sought and the found point
/// in physical space, for each point found by FindPoints.
const Vector &GetDist() const { return gsl_dist; }
};
} // namespace mfem
+1 -1
View File
@@ -40,7 +40,7 @@ struct CeedConstCoeff
struct CeedGridCoeff
{
const GridFunction* coeff;
GridFunction* coeff;
CeedBasis basis;
CeedElemRestriction restr;
CeedVector coeffVector;
+20 -77
View File
@@ -307,7 +307,7 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
if (ir == NULL)
{
int intorder = 2*el.GetOrder();
ir = &IntRules.Get(Tr.GetGeometryType(), intorder);
ir = &IntRules.Get(Tr.FaceGeom, intorder);
}
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -316,11 +316,9 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
IntegrationPoint eip;
Tr.Loc1.Transform(ip, eip);
Tr.SetIntPoint(&ip);
// Use Tr transformation in case Q depends on boundary attribute
Q.Eval(vec, Tr, ip);
vec *= Tr.Weight() * ip.weight;
Tr.Face->SetIntPoint(&ip);
Q.Eval(vec, *Tr.Face, ip);
vec *= Tr.Face->Weight() * ip.weight;
el.CalcShape(eip, shape);
for (int k = 0; k < vdim; k++)
{
@@ -512,7 +510,7 @@ void BoundaryFlowIntegrator::AssembleRHSElementVect(
{
order++;
}
ir = &IntRules.Get(Tr.GetGeometryType(), order);
ir = &IntRules.Get(Tr.FaceGeom, order);
}
shape.SetSize(ndof);
@@ -526,10 +524,8 @@ void BoundaryFlowIntegrator::AssembleRHSElementVect(
Tr.Loc1.Transform(ip, eip);
el.CalcShape(eip, shape);
Tr.SetIntPoint(&ip);
Tr.Face->SetIntPoint(&ip);
// Use Tr.Elem1 transformation for u so that it matches the coefficient
// used with the ConvectionIntegrator and/or the DGTraceIntegrator.
u->Eval(vu, *Tr.Elem1, eip);
if (dim == 1)
@@ -538,12 +534,12 @@ void BoundaryFlowIntegrator::AssembleRHSElementVect(
}
else
{
CalcOrtho(Tr.Jacobian(), nor);
CalcOrtho(Tr.Face->Jacobian(), nor);
}
un = vu * nor;
w = 0.5*alpha*un - beta*fabs(un);
w *= ip.weight*f->Eval(Tr, ip);
w *= ip.weight*f->Eval(*Tr.Elem1, eip);
elvect.Add(w, shape);
}
}
@@ -586,7 +582,7 @@ void DGDirichletLFIntegrator::AssembleRHSElementVect(
{
// a simple choice for the integration order; is this OK?
int order = 2*el.GetOrder();
ir = &IntRules.Get(Tr.GetGeometryType(), order);
ir = &IntRules.Get(Tr.FaceGeom, order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
@@ -595,33 +591,33 @@ void DGDirichletLFIntegrator::AssembleRHSElementVect(
IntegrationPoint eip;
Tr.Loc1.Transform(ip, eip);
Tr.SetIntPoint(&ip);
Tr.Face->SetIntPoint(&ip);
if (dim == 1)
{
nor(0) = 2*eip.x - 1.0;
}
else
{
CalcOrtho(Tr.Jacobian(), nor);
CalcOrtho(Tr.Face->Jacobian(), nor);
}
el.CalcShape(eip, shape);
el.CalcDShape(eip, dshape);
Tr.Elem1->SetIntPoint(&eip);
// compute uD through the face transformation
w = ip.weight * uD->Eval(Tr, ip) / Tr.Elem1->Weight();
w = ip.weight * uD->Eval(*Tr.Face, ip) / Tr.Elem1->Weight();
if (!MQ)
{
if (Q)
{
w *= Q->Eval(Tr, ip);
w *= Q->Eval(*Tr.Elem1, eip);
}
ni.Set(w, nor);
}
else
{
nh.Set(w, nor);
MQ->Eval(mq, Tr, ip);
MQ->Eval(mq, *Tr.Elem1, eip);
mq.MultTranspose(nh, ni);
}
CalcAdjugate(Tr.Elem1->Jacobian(), adjJ);
@@ -680,7 +676,7 @@ void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
if (ir == NULL)
{
const int order = 2*el.GetOrder(); // <-----
ir = &IntRules.Get(Tr.GetGeometryType(), order);
ir = &IntRules.Get(Tr.FaceGeom, order);
}
for (int pi = 0; pi < ir->GetNPoints(); ++pi)
@@ -688,10 +684,11 @@ void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
const IntegrationPoint &ip = ir->IntPoint(pi);
IntegrationPoint eip;
Tr.Loc1.Transform(ip, eip);
Tr.SetIntPoint(&ip);
Tr.Face->SetIntPoint(&ip);
Tr.Elem1->SetIntPoint(&eip);
// Evaluate the Dirichlet b.c. using the face transformation.
uD.Eval(u_dir, Tr, ip);
uD.Eval(u_dir, *Tr.Face, ip);
el.CalcShape(eip, shape);
el.CalcDShape(eip, dshape);
@@ -705,7 +702,7 @@ void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
}
else
{
CalcOrtho(Tr.Jacobian(), nor);
CalcOrtho(Tr.Face->Jacobian(), nor);
}
double wL, wM, jcoef;
@@ -771,58 +768,4 @@ void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
}
}
void VectorQuadratureLFIntegrator::AssembleRHSElementVect(
const FiniteElement &fe, ElementTransformation &Tr, Vector &elvect)
{
const IntegrationRule *ir =
&vqfc.GetQuadFunction().GetSpace()->GetElementIntRule(Tr.ElementNo);
const int nqp = ir->GetNPoints();
const int vdim = vqfc.GetVDim();
const int ndofs = fe.GetDof();
Vector shape(ndofs);
Vector temp(vdim);
elvect.SetSize(vdim * ndofs);
elvect = 0.0;
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint(&ip);
const double w = Tr.Weight() * ip.weight;
vqfc.Eval(temp, Tr, ip);
fe.CalcShape(ip, shape);
for (int ind = 0; ind < vdim; ind++)
{
for (int nd = 0; nd < ndofs; nd++)
{
elvect(nd + ind * ndofs) += w * shape(nd) * temp(ind);
}
}
}
}
void QuadratureLFIntegrator::AssembleRHSElementVect(const FiniteElement &fe,
ElementTransformation &Tr,
Vector &elvect)
{
const IntegrationRule *ir =
&qfc.GetQuadFunction().GetSpace()->GetElementIntRule(Tr.ElementNo);
const int nqp = ir->GetNPoints();
const int ndofs = fe.GetDof();
Vector shape(ndofs);
elvect.SetSize(ndofs);
elvect = 0.0;
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir->IntPoint(q);
Tr.SetIntPoint (&ip);
const double w = Tr.Weight() * ip.weight;
double temp = qfc.Eval(Tr, ip);
fe.CalcShape(ip, shape);
shape *= (w * temp);
elvect += shape;
}
}
}
+1 -64
View File
@@ -36,7 +36,7 @@ public:
FaceElementTransformations &Tr,
Vector &elvect);
virtual void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
const IntegrationRule* GetIntRule() { return IntRule; }
virtual ~LinearFormIntegrator() { }
@@ -426,69 +426,6 @@ public:
Vector &elvect);
};
/** Class for domain integration of L(v) := (f, v), where
f=(f1,...,fn) and v=(v1,...,vn). that makes use of
VectorQuadratureFunctionCoefficient*/
class VectorQuadratureLFIntegrator : public LinearFormIntegrator
{
private:
VectorQuadratureFunctionCoefficient &vqfc;
public:
VectorQuadratureLFIntegrator(VectorQuadratureFunctionCoefficient &vqfc,
const IntegrationRule *ir)
: LinearFormIntegrator(ir), vqfc(vqfc)
{
if (ir)
{
MFEM_WARNING("Integration rule not used in this class. "
"The QuadratureFunction integration rules are used instead");
}
}
using LinearFormIntegrator::AssembleRHSElementVect;
virtual void AssembleRHSElementVect(const FiniteElement &fe,
ElementTransformation &Tr,
Vector &elvect);
virtual void SetIntRule(const IntegrationRule *ir)
{
MFEM_WARNING("Integration rule not used in this class. "
"The QuadratureFunction integration rules are used instead");
}
};
/** Class for domain integration L(v) := (f, v) that makes use
of QuadratureFunctionCoefficient. */
class QuadratureLFIntegrator : public LinearFormIntegrator
{
private:
QuadratureFunctionCoefficient &qfc;
public:
QuadratureLFIntegrator(QuadratureFunctionCoefficient &qfc,
const IntegrationRule *ir)
: LinearFormIntegrator(ir), qfc(qfc)
{
if (ir)
{
MFEM_WARNING("Integration rule not used in this class. "
"The QuadratureFunction integration rules are used instead");
}
}
using LinearFormIntegrator::AssembleRHSElementVect;
virtual void AssembleRHSElementVect(const FiniteElement &fe,
ElementTransformation &Tr,
Vector &elvect);
virtual void SetIntRule(const IntegrationRule *ir)
{
MFEM_WARNING("Integration rule not used in this class. "
"The QuadratureFunction integration rules are used instead");
}
};
}
#endif
+420 -2
View File
@@ -16,6 +16,7 @@
#include "fem.hpp"
#include <iostream>
#include <limits>
#include <string>
#include "../general/forall.hpp"
using namespace std;
@@ -78,6 +79,229 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, std::istream &input)
fes = pfes;
}
ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf,
const char *_filename)
: GridFunction(pf), pfes(pf)
{
MPI_Comm fes_comm;
int fes_rank, n_fes_ranks;
fes_comm = pfes->GetComm();
MPI_Comm_size(fes_comm, &n_fes_ranks);
MPI_Comm_rank(fes_comm, &fes_rank);
std::string filename(_filename);
std::string file_prefix;
std::string file_ext;
{
size_t i = filename.rfind('.', filename.length());
if (i != string::npos)
{
file_prefix = (filename.substr(0, i));
file_ext = (filename.substr(i, filename.length() - i));
}
}
int nfiles = 1;
if (fes_rank == 0)
{
int n_rfes_ranks;
int tmp[2];
std::string mpi_filename;
size_t i = filename.rfind('.', filename.length());
if (i != string::npos)
{
mpi_filename = file_prefix + to_string(0) + file_ext;
}
else
{
mpi_filename = filename + to_string(0);
}
MPI_File fh;
MPI_File_open(MPI_COMM_SELF, mpi_filename.c_str(), MPI_MODE_RDONLY,
MPI_INFO_NULL, &fh);
MPI_File_read_at(fh, 0, tmp, 2, MPI_INT, MPI_STATUS_IGNORE);
MPI_File_close(&fh);
n_rfes_ranks = tmp[0];
nfiles = tmp[1];
MFEM_ASSERT(n_fes_ranks == n_rfes_ranks,
"ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf,"
" const char *_filename):\n"
"\tThe number of MPI ranks used to save the GridFunction is\n"
"\tnot the same as the number used to load it!");
}
MPI_Bcast(&nfiles, 1, MPI_INT, 0, fes_comm);
int color = fes_rank * nfiles / n_fes_ranks;
MPI_Comm file_comm;
MPI_Comm_split(fes_comm, color, fes_rank, &file_comm);
int file_rank, n_file_ranks;
MPI_Comm_size(file_comm, &n_file_ranks);
MPI_Comm_rank(file_comm, &file_rank);
std::string mpi_filename;
{
size_t i = filename.rfind('.', filename.length());
if (i != string::npos) {
mpi_filename = file_prefix + std::to_string(color) + file_ext;
}
else
{
mpi_filename = filename + std::to_string(color);
}
}
MPI_File fh;
MPI_File_open(file_comm, mpi_filename.c_str(), MPI_MODE_RDONLY,
MPI_INFO_NULL, &fh);
int *dof_counts = new int[5*n_file_ranks];
int **nv = new int*[n_file_ranks];
int **nvdofs = new int*[n_file_ranks];
int **nedofs = new int*[n_file_ranks];
int **nfdofs = new int*[n_file_ranks];
int **nrdofs = new int*[n_file_ranks];
for (int i = 0; i < n_file_ranks; ++i)
{
nv[i] = &dof_counts[i*5+0];
nvdofs[i] = &dof_counts[i*5+1];
nedofs[i] = &dof_counts[i*5+2];
nfdofs[i] = &dof_counts[i*5+3];
nrdofs[i] = &dof_counts[i*5+4];
}
*nv[file_rank] = pfes->GetVSize();
*nvdofs[file_rank] = pfes->GetNVDofs();
*nedofs[file_rank] = pfes->GetNEDofs();
*nfdofs[file_rank] = pfes->GetNFDofs();
int vdim = pfes->GetVDim();
*nrdofs[file_rank] = *nv[file_rank] / vdim - *nvdofs[file_rank] -
*nedofs[file_rank] - *nfdofs[file_rank];
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &dof_counts[0], 5,
MPI_INT, file_comm);
double *data_ = HostWrite();
MPI_Offset header_offset = 0;
header_offset += 2 * sizeof(int);
MPI_Offset v_offset, e_offset, f_offset, r_offset;
int total_vdofs = 0, total_edofs = 0, total_fdofs = 0, total_rdofs = 0;
int total_scalar_dofs = 0;
for (int i = 0; i < n_file_ranks; ++i)
{
total_vdofs += *nvdofs[i];
total_edofs += *nedofs[i];
total_fdofs += *nfdofs[i];
total_rdofs += *nrdofs[i];
total_scalar_dofs += *nv[i];
}
total_scalar_dofs /= vdim;
if (pfes->GetOrdering() == Ordering::byNODES)
{
for (int d = 0; d < vdim; ++d)
{
int v_data_offset = 0 + *nv[file_rank] * d / vdim ;
int e_data_offset = v_data_offset + *nvdofs[file_rank];
int f_data_offset = e_data_offset + *nedofs[file_rank];
int r_data_offset = f_data_offset + *nfdofs[file_rank];
v_offset = header_offset;
e_offset = header_offset;
f_offset = header_offset;
r_offset = header_offset;
v_offset += total_scalar_dofs * d * sizeof(double);
e_offset += (total_vdofs + total_scalar_dofs * d) * sizeof(double);
f_offset += (total_vdofs + total_edofs +
total_scalar_dofs * d) * sizeof(double);
r_offset += (total_vdofs + total_edofs + total_fdofs +
total_scalar_dofs * d) * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double);
e_offset += *nedofs[i] * sizeof(double);
f_offset += *nfdofs[i] * sizeof(double);
r_offset += *nrdofs[i] * sizeof(double);
}
MPI_File_read_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
}
else
{
v_offset = header_offset;
e_offset = v_offset + total_vdofs * vdim * sizeof(double);
f_offset = e_offset + total_edofs * vdim * sizeof(double);
r_offset = f_offset + total_fdofs * vdim * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double) * vdim;
e_offset += *nedofs[i] * sizeof(double) * vdim;
f_offset += *nfdofs[i] * sizeof(double) * vdim;
r_offset += *nrdofs[i] * sizeof(double) * vdim;
}
int v_data_offset = 0;
int e_data_offset = v_data_offset + *nvdofs[file_rank] * vdim;
int f_data_offset = e_data_offset + *nedofs[file_rank] * vdim;
int r_data_offset = f_data_offset + *nfdofs[file_rank] * vdim;
MPI_File_read_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
MPI_File_close(&fh);
MPI_Comm_free(&file_comm);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
delete[] dof_counts;
delete[] nv;
delete[] nvdofs;
delete[] nedofs;
delete[] nfdofs;
delete[] nrdofs;
}
void ParGridFunction::Update()
{
face_nbr_data.Destroy();
@@ -232,8 +456,7 @@ void ParGridFunction::ExchangeFaceNbrData()
auto d_send_data = send_data.Write();
MFEM_FORALL(i, send_data.Size(),
{
const int ldof = d_send_ldof[i];
d_send_data[i] = d_data[ldof >= 0 ? ldof : -1-ldof];
d_send_data[i] = d_data[d_send_ldof[i]];
});
bool mpi_gpu_aware = Device::GetGPUAwareMPI();
@@ -519,6 +742,201 @@ void ParGridFunction::Save(adios2stream &out,
}
#endif
void ParGridFunction::Save(const char *_filename, const int nfiles)
{
MPI_Comm fes_comm;
int fes_rank, n_fes_ranks;
fes_comm = pfes->GetComm();
MPI_Comm_size(fes_comm, &n_fes_ranks);
MPI_Comm_rank(fes_comm, &fes_rank);
int color = fes_rank * nfiles / n_fes_ranks;
MPI_Comm file_comm;
MPI_Comm_split(fes_comm, color, fes_rank, &file_comm);
int file_rank, n_file_ranks;
MPI_Comm_size(file_comm, &n_file_ranks);
MPI_Comm_rank(file_comm, &file_rank);
std::string filename(_filename);
std::string file_prefix;
std::string file_ext;
std::string mpi_filename;
{
size_t i = filename.rfind('.', filename.length());
if (i != string::npos)
{
file_prefix = (filename.substr(0, i));
file_ext = (filename.substr(i, filename.length() - i));
mpi_filename = file_prefix + std::to_string(color) + file_ext;
}
else
{
mpi_filename = filename + std::to_string(color);
}
}
MPI_File fh;
MPI_File_open(file_comm, mpi_filename.c_str(), MPI_MODE_CREATE |
MPI_MODE_WRONLY,
MPI_INFO_NULL, &fh);
int *dof_counts = new int[5*n_file_ranks];
int **nv = new int*[n_file_ranks];
int **nvdofs = new int*[n_file_ranks];
int **nedofs = new int*[n_file_ranks];
int **nfdofs = new int*[n_file_ranks];
int **nrdofs = new int*[n_file_ranks];
for (int i = 0; i < n_file_ranks; ++i)
{
nv[i] = &dof_counts[i*5+0];
nvdofs[i] = &dof_counts[i*5+1];
nedofs[i] = &dof_counts[i*5+2];
nfdofs[i] = &dof_counts[i*5+3];
nrdofs[i] = &dof_counts[i*5+4];
}
*nv[file_rank] = pfes->GetVSize();
*nvdofs[file_rank] = pfes->GetNVDofs();
*nedofs[file_rank] = pfes->GetNEDofs();
*nfdofs[file_rank] = pfes->GetNFDofs();
int vdim = pfes->GetVDim();
*nrdofs[file_rank] = *nv[file_rank] / vdim - *nvdofs[file_rank] -
*nedofs[file_rank] - *nfdofs[file_rank];
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &dof_counts[0], 5,
MPI_INT, file_comm);
double *data_ = const_cast<double*>(HostRead());
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
MPI_Offset header_offset = 0;
if (file_rank == 0)
{
int tmp[] = {n_fes_ranks, nfiles};
MPI_File_write_at(fh, header_offset, &tmp, 2, MPI_INT,
MPI_STATUS_IGNORE);
}
header_offset += 2 * sizeof(int);
MPI_Offset v_offset, e_offset, f_offset, r_offset;
int total_vdofs = 0, total_edofs = 0, total_fdofs = 0, total_rdofs = 0;
int total_scalar_dofs = 0;
for (int i = 0; i < n_file_ranks; ++i)
{
total_vdofs += *nvdofs[i];
total_edofs += *nedofs[i];
total_fdofs += *nfdofs[i];
total_rdofs += *nrdofs[i];
total_scalar_dofs += *nv[i];
}
total_scalar_dofs /= vdim;
if (pfes->GetOrdering() == Ordering::byNODES)
{
for (int d = 0; d < vdim; ++d)
{
int v_data_offset = 0 + *nv[file_rank] * d / vdim ;
int e_data_offset = v_data_offset + *nvdofs[file_rank];
int f_data_offset = e_data_offset + *nedofs[file_rank];
int r_data_offset = f_data_offset + *nfdofs[file_rank];
v_offset = header_offset;
e_offset = header_offset;
f_offset = header_offset;
r_offset = header_offset;
v_offset += total_scalar_dofs * d * sizeof(double);
e_offset += (total_vdofs + total_scalar_dofs * d) * sizeof(double);
f_offset += (total_vdofs + total_edofs +
total_scalar_dofs * d) * sizeof(double);
r_offset += (total_vdofs + total_edofs + total_fdofs +
total_scalar_dofs * d) * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double);
e_offset += *nedofs[i] * sizeof(double);
f_offset += *nfdofs[i] * sizeof(double);
r_offset += *nrdofs[i] * sizeof(double);
}
MPI_File_write_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
}
else
{
v_offset = header_offset;
e_offset = v_offset + total_vdofs * vdim * sizeof(double);
f_offset = e_offset + total_edofs * vdim * sizeof(double);
r_offset = f_offset + total_fdofs * vdim * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double) * vdim;
e_offset += *nedofs[i] * sizeof(double) * vdim;
f_offset += *nfdofs[i] * sizeof(double) * vdim;
r_offset += *nrdofs[i] * sizeof(double) * vdim;
}
int v_data_offset = 0;
int e_data_offset = v_data_offset + *nvdofs[file_rank] * vdim;
int f_data_offset = e_data_offset + *nedofs[file_rank] * vdim;
int r_data_offset = f_data_offset + *nfdofs[file_rank] * vdim;
MPI_File_write_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
MPI_File_close(&fh);
MPI_Comm_free(&file_comm);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
delete[] dof_counts;
delete[] nv;
delete[] nvdofs;
delete[] nedofs;
delete[] nfdofs;
delete[] nrdofs;
}
void ParGridFunction::SaveAsOne(std::ostream &out)
{
int i, p;
+21
View File
@@ -83,6 +83,13 @@ public:
constructed. The new ParGridFunction assumes ownership of both. */
ParGridFunction(ParMesh *pmesh, std::istream &input);
/// Construct a ParGridFunction by loading a ParGridFunction saved using
/// ParGridFunction::Save(char *filename, int nfiles).
/** The parallel space @a *pf and the space used by the GridFunction saved
in @a *filename should match. The number of ranks used when loading the
ParGridFunction must be the same as when it was saved. */
ParGridFunction(ParFiniteElementSpace *pf, const char *filename);
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use ParFiniteElementSpace%s
that have the same size.
@@ -324,6 +331,20 @@ public:
const adios2stream::data_type type = adios2stream::data_type::point_data) const;
#endif
/** Save the local grid functions to n number of files, where each file will
contain the grid functions from potentially multiple ranks. This is
similar to the syncIO approach from "Fu, Jing, et al. 'Scalable parallel
I/O alternatives for massively parallel partitioned solver systems.'
2010 IEEE International Symposium on Parallel & Distributed Processing,
Workshops and Phd Forum (IPDPSW). IEEE, 2010."
@param[in] filename - filename for output files with extension
@param[in] nfiles - number of files to write using MPI-IO
@note - takes into account the signs of the local dofs.
@note - writes a binary file without the FESpace header; the saved file
should only be loaded by the accompanying constructor:
ParGridFunction(ParFiniteElementSpace *pf, const char *filename) */
void Save(const char *filename, const int nfiles = 1);
/// Merge the local grid functions
void SaveAsOne(std::ostream &out = mfem::out);
+17 -62
View File
@@ -168,27 +168,6 @@ void ElementRestriction::Mult(const Vector& x, Vector& y) const
});
}
void ElementRestriction::MultUnsigned(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_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.Write(), nd, vd, ne);
auto d_gatherMap = gatherMap.Read();
MFEM_FORALL(i, dof*ne,
{
const int gid = d_gatherMap[i];
const int j = gid >= 0 ? gid : -1-gid;
for (int c = 0; c < vd; ++c)
{
d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
}
});
}
void ElementRestriction::MultTranspose(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
@@ -987,51 +966,27 @@ void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
const int dofs = nfdofs;
auto d_offsets = offsets.Read();
auto d_indices = gather_indices.Read();
if (m == L2FaceValues::DoubleValued)
auto d_x = Reshape(x.Read(), nd, vd, 2, nf);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
auto d_x = Reshape(x.Read(), nd, vd, 2, nf);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i + 1];
for (int c = 0; c < vd; ++c)
{
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i + 1];
for (int c = 0; c < vd; ++c)
double dofValue = 0;
for (int j = offset; j < nextOffset; ++j)
{
double dofValue = 0;
for (int j = offset; j < nextOffset; ++j)
{
int idx_j = d_indices[j];
bool isE1 = idx_j < dofs;
idx_j = isE1 ? idx_j : idx_j - dofs;
dofValue += isE1 ?
d_x(idx_j % nd, c, 0, idx_j / nd)
:d_x(idx_j % nd, c, 1, idx_j / nd);
}
d_y(t?c:i,t?i:c) += dofValue;
int idx_j = d_indices[j];
bool isE1 = idx_j < dofs;
idx_j = isE1 ? idx_j : idx_j - dofs;
dofValue += isE1 ?
d_x(idx_j % nd, c, 0, idx_j / nd)
:d_x(idx_j % nd, c, 1, idx_j / nd);
}
});
}
else
{
auto d_x = Reshape(x.Read(), nd, vd, nf);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i + 1];
for (int c = 0; c < vd; ++c)
{
double dofValue = 0;
for (int j = offset; j < nextOffset; ++j)
{
int idx_j = d_indices[j];
dofValue += d_x(idx_j % nd, c, idx_j / nd);
}
d_y(t?c:i,t?i:c) += dofValue;
}
});
}
d_y(t?c:i,t?i:c) += dofValue;
}
});
}
int ToLexOrdering(const int dim, const int face_id, const int size1d,
-2
View File
@@ -47,8 +47,6 @@ public:
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
/// Compute Mult without applying signs based on DOF orientations.
void MultUnsigned(const Vector &x, Vector &y) const;
/// Compute MultTranspose without applying signs based on DOF orientations.
void MultTransposeUnsigned(const Vector &x, Vector &y) const;
+138 -208
View File
@@ -13,7 +13,6 @@
#define MFEM_TEMPLATE_BILINEAR_FORM
#include "../config/tconfig.hpp"
#include "../linalg/simd.hpp"
#include "../linalg/ttensor.hpp"
#include "bilinearform.hpp"
#include "tevaluator.hpp"
@@ -31,13 +30,9 @@ namespace mfem
template <typename meshType, typename solFESpace,
typename IR, typename IntegratorType,
typename solVecLayout_t = ScalarLayout,
typename complex_t = double, typename real_t = double,
typename impl_traits_t = AutoSIMDTraits<complex_t,real_t> >
typename complex_t = double, typename real_t = double>
class TBilinearForm : public Operator
{
public:
typedef impl_traits_t impl_traits_type;
protected:
typedef complex_t complex_type;
typedef real_t real_type;
@@ -53,36 +48,26 @@ protected:
static const int dofs = solFE_type::dofs;
static const int vdim = solVecLayout_t::vec_dim;
static const int qpts = IR::qpts;
static const int AB = impl_traits_t::align_bytes;
static const int SS = impl_traits_t::simd_size;
static const int BE = impl_traits_t::batch_size;
static const int TE = SS*BE;
typedef typename impl_traits_t::vcomplex_t vcomplex_t;
typedef typename impl_traits_t::vreal_t vreal_t;
typedef IntegratorType integ_t;
typedef typename integ_t::coefficient_type coeff_t;
typedef typename integ_t::template kernel<sdim,dim,vcomplex_t>::type kernel_t;
typedef typename integ_t::template kernel<sdim,dim,complex_t>::type kernel_t;
typedef typename kernel_t::template p_asm_data<qpts>::type p_assembled_t;
typedef typename kernel_t::template f_asm_data<qpts>::type f_assembled_t;
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,impl_traits_t>::Type coeff_eval_t;
typedef TElementTransformation<meshType,IR,real_t> Trans_t;
struct T_result
template <int NE> struct T_result
{
static const int EvalOps =
Trans_t::template Get<coeff_t,kernel_t>::EvalOps;
typedef typename Trans_t::template Result<EvalOps,impl_traits_t> Type;
typedef typename Trans_t::template Result<EvalOps,NE> Type;
};
typedef FieldEvaluator<solFESpace,solVecLayout_t,IR,
complex_t,real_t> solFieldEval;
struct S_spec
template <int BE> struct S_spec
{
typedef typename solFieldEval::template Spec<kernel_t,impl_traits_t> Spec;
typedef typename solFieldEval::template Spec<kernel_t,BE> Spec;
typedef typename Spec::DataType DataType;
typedef typename Spec::ElementMatrix ElementMatrix;
};
@@ -101,7 +86,7 @@ protected:
coeff_t coeff;
Memory<p_assembled_t> assembled_data;
p_assembled_t *assembled_data;
const FiniteElementSpace &in_fes;
@@ -116,17 +101,13 @@ public:
solVecLayout(sol_fes),
int_rule(),
coeff(integ.coeff),
assembled_data(),
assembled_data(NULL),
in_fes(sol_fes)
{
assembled_data.Reset(AB == 64 ? MemoryType::HOST_64 :
AB == 32 ? MemoryType::HOST_32 :
MemoryType::HOST);
}
{ }
virtual ~TBilinearForm()
{
assembled_data.Delete();
delete [] assembled_data;
}
/// Get the input finite element space prolongation matrix
@@ -138,9 +119,10 @@ public:
virtual void Mult(const Vector &x, Vector &y) const
{
if (!assembled_data.Empty())
if (assembled_data)
{
MultAssembled(x, y);
const int num_elem = 1;
MultAssembled<num_elem>(x, y);
}
else
{
@@ -153,6 +135,10 @@ public:
{
y = 0.0;
const int BE = 1; // batch-size of elements
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,BE>::Type coeff_eval_t;
// For better performance, create stack copies of solFES, and solEval
// inside 'solFEval'. The element-transformation 'T' also copies the
// meshFES, meshEval, etc internally.
@@ -163,29 +149,26 @@ public:
coeff_eval_t wQ(int_rule, coeff);
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++)
{
#if 0
typename S_spec::DataType R;
typename S_spec<BE>::DataType R;
solFEval.Eval(el, R);
typename T_result::Type F;
typename T_result<BE>::Type F;
T.Eval(el, F);
#else
typename T_result::Type F;
typename T_result<BE>::Type F;
T.Eval(el, F);
typename S_spec::DataType R;
typename S_spec<BE>::DataType R;
solFEval.Eval(el, R);
#endif
typename coeff_eval_t::result_t res;
wQ.Eval(F, res);
for (int k = 0; k < BE; k++)
{
kernel_t::Action(k, F, wQ, res, R);
}
kernel_t::Action(0, F, wQ, res, R);
solFEval.template Assemble<true>(R);
}
@@ -194,18 +177,21 @@ public:
// Partial assembly of quadrature point data
void Assemble()
{
const int BE = 1; // batch-size of elements
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,BE>::Type coeff_eval_t;
Trans_t T(mesh, meshEval);
coeff_eval_t wQ(int_rule, coeff);
const int NE = mesh.GetNE();
if (assembled_data.Empty())
if (!assembled_data)
{
const int size = ((NE+TE-1)/TE)*BE;
assembled_data.New(size, assembled_data.GetMemoryType());
assembled_data = new p_assembled_t[NE];
}
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++) // BE == 1
{
typename T_result::Type F;
typename T_result<BE>::Type F;
T.Eval(el, F);
typename coeff_eval_t::result_t res;
@@ -213,26 +199,28 @@ public:
for (int k = 0; k < BE; k++)
{
kernel_t::Assemble(k, F, wQ, res, assembled_data[el/SS+k]);
kernel_t::Assemble(k, F, wQ, res, assembled_data[el+k]);
}
}
}
template <int num_elem>
inline MFEM_ALWAYS_INLINE
void ElementAddMultAssembled(int el, solFieldEval &solFEval) const
{
typename S_spec::DataType R;
typename S_spec<num_elem>::DataType R;
solFEval.Eval(el, R);
for (int k = 0; k < BE; k++)
for (int k = 0; k < num_elem; k++)
{
kernel_t::MultAssembled(k, assembled_data[el/SS+k], R);
kernel_t::MultAssembled(k, assembled_data[el+k], R);
}
solFEval.template Assemble<true>(R);
}
// complex_t = double
template <int num_elem>
void MultAssembled(const Vector &x, Vector &y) const
{
y = 0.0;
@@ -241,9 +229,14 @@ public:
x.GetData(), y.GetData());
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el += TE)
const int bNE = NE-NE%num_elem;
for (int el = 0; el < bNE; el += num_elem)
{
ElementAddMultAssembled(el, solFEval);
ElementAddMultAssembled<num_elem>(el, solFEval);
}
for (int el = bNE; el < NE; el++)
{
ElementAddMultAssembled<1>(el, solFEval);
}
}
@@ -256,10 +249,10 @@ public:
solVecLayout_type solVecLayout(this->solVecLayout);
solFESpace solFES(this->solFES);
TTensor3<dofs,vdim,BE,vcomplex_t> xy_dof;
TTensor3<dofs,vdim,1,complex_t> xy_dof;
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++)
{
solFES.SetElement(el);
@@ -273,21 +266,17 @@ public:
{
typedef typename meshType::FESpace_type meshFESpace;
meshFESpace meshFES(mesh.t_fes);
typedef TTensor3<meshFE_type::dofs,sdim,BE,vreal_t> lnodes_t;
typedef TTensor3<meshFE_type::dofs,sdim,1,real_t> lnodes_t;
const int NE = mesh.GetNE();
// TODO: How do we make sure that this array is aligned properly, AND
// the compiler knows that it is aligned? => ALIGN_32|ALIGN_64 when ready
const int NVE = (NE+TE-1)/TE;
vreal_t *vsNodes = new vreal_t[lnodes_t::size*NVE];
sNodes.NewDataAndSize(vsNodes[0].vec, (lnodes_t::size*SS)*NVE);
sNodes.MakeDataOwner();
for (int el = 0; el < NE; el += TE)
sNodes.SetSize(lnodes_t::size*NE);
real_t *lNodes = sNodes.GetData();
for (int el = 0; el < NE; el++)
{
meshFES.SetElement(el);
meshFES.VectorExtract(mesh.node_layout, mesh.Nodes,
lnodes_t::layout, vsNodes);
vsNodes += lnodes_t::size;
lnodes_t::layout, lNodes);
lNodes += lnodes_t::size;
}
}
@@ -295,51 +284,45 @@ public:
// real_t = double
void AssembleFromSerializedNodes(const Vector &sNodes)
{
Trans_t T(mesh, meshEval);
const int BE = 1; // batch-size of elements
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,BE>::Type coeff_eval_t;
Trans_t T(this->mesh, this->meshEval);
coeff_eval_t wQ(int_rule, coeff);
const int NE = mesh.GetNE();
if (assembled_data.Empty())
if (!assembled_data)
{
const int size = ((NE+TE-1)/TE)*BE;
assembled_data.New(size, assembled_data.GetMemoryType());
assembled_data = new p_assembled_t[NE];
}
const vreal_t *vsNodes = (const vreal_t*)(sNodes.GetData());
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++)
{
typename T_result::Type F;
T.EvalSerialized(el, vsNodes, F);
typename T_result<BE>::Type F;
T.EvalSerialized(el, sNodes.GetData(), F);
typename coeff_eval_t::result_t res;
wQ.Eval(F, res);
for (int k = 0; k < BE; k++)
{
kernel_t::Assemble(k, F, wQ, res, assembled_data[el/SS+k]);
}
kernel_t::Assemble(0, F, wQ, res, assembled_data[el]);
}
}
// complex_t = double
void Serialize(const Vector &x, Vector &sx) const
{
typedef TTensor3<dofs,vdim,BE,vcomplex_t> vdof_data_t;
solVecLayout_t solVecLayout(this->solVecLayout);
typedef TTensor3<dofs,vdim,1,complex_t> vdof_data_t;
solFESpace solFES(this->solFES);
const int NE = mesh.GetNE();
// TODO: How do we make sure that this array is aligned properly, AND
// the compiler knows that it is aligned? => ALIGN_32|ALIGN_64 when ready
const int NVE = (NE+TE-1)/TE;
vreal_t *vsx = new vreal_t[vdof_data_t::size*NVE];
sx.NewDataAndSize(vsx[0].vec, (vdof_data_t::size*SS)*NVE);
sx.MakeDataOwner();
for (int el = 0; el < NE; el += TE)
sx.SetSize(vdim*dofs*NE);
complex_t *loc_sx = sx.GetData();
for (int el = 0; el < NE; el++)
{
solFES.SetElement(el);
solFES.VectorExtract(solVecLayout, x, vdof_data_t::layout, vsx);
vsx += vdof_data_t::size;
solFES.VectorExtract(solVecLayout, x, vdof_data_t::layout, loc_sx);
loc_sx += vdim*dofs;
}
}
@@ -350,23 +333,19 @@ public:
solFieldEval solFEval(solFES, solEval, solVecLayout, NULL, NULL);
const int NE = mesh.GetNE();
const vreal_t *vsx = (const vreal_t*)(sx.GetData());
vreal_t *vsy = (vreal_t*)(sy.GetData());
for (int el = 0; el < NE; el += TE)
const complex_t *loc_sx = sx.GetData();
complex_t *loc_sy = sy.GetData();
for (int el = 0; el < NE; el++)
{
typename S_spec::DataType R;
solFEval.EvalSerialized(vsx, R);
typename S_spec<1>::DataType R;
solFEval.EvalSerialized(loc_sx, R);
for (int k = 0; k < BE; k++)
{
kernel_t::MultAssembled(k, assembled_data[el/SS+k], R);
}
kernel_t::MultAssembled(0, assembled_data[el], R);
solFEval.template AssembleSerialized<false>(R, vsy);
solFEval.template AssembleSerialized<false>(R, loc_sy);
vsx += vdim*dofs*BE;
vsy += vdim*dofs*BE;
loc_sx += vdim*dofs;
loc_sy += vdim*dofs;
}
}
#endif // MFEM_TEMPLATE_ENABLE_SERIALIZE
@@ -375,6 +354,10 @@ public:
// complex_t = double
void AssembleMatrix(SparseMatrix &M) const
{
const int BE = 1; // batch-size of elements
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,BE>::Type coeff_eval_t;
Trans_t T(mesh, meshEval);
solFESpace solFES(this->solFES);
solShapeEval solEval(this->solEval);
@@ -382,39 +365,29 @@ public:
coeff_eval_t wQ(int_rule, coeff);
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++)
{
f_assembled_t asm_qpt_data[BE];
f_assembled_t asm_qpt_data;
{
typename T_result::Type F;
typename T_result<BE>::Type F;
T.Eval(el, F);
typename coeff_eval_t::result_t res;
wQ.Eval(F, res);
for (int k = 0; k < BE; k++)
{
kernel_t::Assemble(k, F, wQ, res, asm_qpt_data[k]);
}
kernel_t::Assemble(0, F, wQ, res, asm_qpt_data);
}
// For now, when vdim > 1, assume block-diagonal matrix with the same
// diagonal block for all components.
for (int k = 0; k < BE; k++)
TMatrix<dofs,dofs> M_loc;
S_spec<BE>::ElementMatrix::Compute(
asm_qpt_data.layout, asm_qpt_data, M_loc.layout, M_loc, solEval);
solFES.SetElement(el);
for (int bi = 0; bi < vdim; bi++)
{
const int el_k = el+SS*k;
if (el_k >= NE) { break; }
TMatrix<dofs,dofs,vcomplex_t> M_loc;
S_spec::ElementMatrix::Compute(
asm_qpt_data[k].layout, asm_qpt_data[k], M_loc.layout, M_loc,
solEval);
solFES.SetElement(el_k);
for (int bi = 0; bi < vdim; bi++)
{
solFES.AssembleBlock(bi, bi, solVecLayout, M_loc, M);
}
solFES.AssembleBlock(bi, bi, solVecLayout, M_loc, M);
}
}
}
@@ -423,52 +396,37 @@ public:
// complex_t = double
void AssembleMatrix(DenseTensor &M) const
{
const int BE = 1; // batch-size of elements
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,BE>::Type coeff_eval_t;
Trans_t T(mesh, meshEval);
solShapeEval solEval(this->solEval);
coeff_eval_t wQ(int_rule, coeff);
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++)
{
f_assembled_t asm_qpt_data[BE];
f_assembled_t asm_qpt_data;
{
typename T_result::Type F;
typename T_result<BE>::Type F;
T.Eval(el, F);
typename coeff_eval_t::result_t res;
wQ.Eval(F, res);
for (int k = 0; k < BE; k++)
{
kernel_t::Assemble(k, F, wQ, res, asm_qpt_data[k]);
}
kernel_t::Assemble(0, F, wQ, res, asm_qpt_data);
}
// For now, when vdim > 1, assume block-diagonal matrix with the same
// diagonal block for all components.
// M is assumed to be (dof x dof x NE).
for (int k = 0; k < BE; k++)
{
const int el_k = el+SS*k;
if (el_k >= NE) { break; }
TMatrix<dofs,dofs> M_loc;
S_spec<BE>::ElementMatrix::Compute(
asm_qpt_data.layout, asm_qpt_data, M_loc.layout, M_loc, solEval);
TMatrix<dofs,dofs,vcomplex_t> M_loc;
S_spec::ElementMatrix::Compute(
asm_qpt_data[k].layout, asm_qpt_data[k], M_loc.layout, M_loc,
solEval);
for (int s = 0; s < SS && el_k+s < NE; s++)
{
complex_t *M_data = M.GetData(el_k+s);
for (int j = 0; j < dofs; j++)
{
for (int i = 0; i < dofs; i++)
{
M_data[j+dofs*i] = M_loc(i,j)[s];
}
}
}
}
complex_t *M_data = M.GetData(el);
M_loc.template AssignTo<AssignOp::Set>(M_data);
}
}
@@ -476,6 +434,10 @@ public:
// complex_t = double
void AssembleBilinearForm(BilinearForm &a) const
{
const int BE = 1; // batch-size of elements
typedef typename kernel_t::template
CoefficientEval<IR,coeff_t,BE>::Type coeff_eval_t;
Trans_t T(mesh, meshEval);
solShapeEval solEval(this->solEval);
coeff_eval_t wQ(int_rule, coeff);
@@ -486,87 +448,55 @@ public:
DenseMatrix M_loc_perm(dofs*vdim,dofs*vdim); // initialized with zeros
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el += TE)
for (int el = 0; el < NE; el++)
{
f_assembled_t asm_qpt_data[BE];
f_assembled_t asm_qpt_data;
{
typename T_result::Type F;
typename T_result<BE>::Type F;
T.Eval(el, F);
typename coeff_eval_t::result_t res;
wQ.Eval(F, res);
for (int k = 0; k < BE; k++)
{
kernel_t::Assemble(k, F, wQ, res, asm_qpt_data[k]);
}
kernel_t::Assemble(0, F, wQ, res, asm_qpt_data);
}
// For now, when vdim > 1, assume block-diagonal matrix with the same
// diagonal block for all components.
for (int k = 0; k < BE; k++)
TMatrix<dofs,dofs> M_loc;
S_spec<BE>::ElementMatrix::Compute(
asm_qpt_data.layout, asm_qpt_data, M_loc.layout, M_loc, solEval);
if (dof_map) // switch from tensor-product ordering
{
const int el_k = el+SS*k;
if (el_k >= NE) { break; }
TMatrix<dofs,dofs,vcomplex_t> M_loc;
S_spec::ElementMatrix::Compute(
asm_qpt_data[k].layout, asm_qpt_data[k], M_loc.layout, M_loc,
solEval);
if (dof_map) // switch from tensor-product ordering
for (int i = 0; i < dofs; i++)
{
for (int s = 0; s < SS && el_k+s < NE; s++)
for (int j = 0; j < dofs; j++)
{
for (int i = 0; i < dofs; i++)
{
for (int j = 0; j < dofs; j++)
{
M_loc_perm(dof_map_[i],dof_map_[j]) = M_loc(i,j)[s];
}
}
for (int bi = 1; bi < vdim; bi++)
{
M_loc_perm.CopyMN(M_loc_perm, dofs, dofs, 0, 0,
bi*dofs, bi*dofs);
}
a.AssembleElementMatrix(el_k+s, M_loc_perm, vdofs);
M_loc_perm(dof_map_[i],dof_map_[j]) = M_loc(i,j);
}
}
else if (SS == 1)
for (int bi = 1; bi < vdim; bi++)
{
DenseMatrix DM(M_loc.data[0].vec, dofs, dofs);
if (vdim == 1)
{
a.AssembleElementMatrix(el_k, DM, vdofs);
}
else
{
for (int bi = 0; bi < vdim; bi++)
{
M_loc_perm.CopyMN(DM, dofs, dofs, 0, 0, bi*dofs, bi*dofs);
}
a.AssembleElementMatrix(el_k, M_loc_perm, vdofs);
}
M_loc_perm.CopyMN(M_loc_perm, dofs, dofs, 0, 0,
bi*dofs, bi*dofs);
}
a.AssembleElementMatrix(el, M_loc_perm, vdofs);
}
else
{
DenseMatrix DM(M_loc.data, dofs, dofs);
if (vdim == 1)
{
a.AssembleElementMatrix(el, DM, vdofs);
}
else
{
for (int s = 0; s < SS && el_k+s < NE; s++)
for (int bi = 0; bi < vdim; bi++)
{
for (int i = 0; i < dofs; i++)
{
for (int j = 0; j < dofs; j++)
{
M_loc_perm(i,j) = M_loc(i,j)[s];
}
}
for (int bi = 1; bi < vdim; bi++)
{
M_loc_perm.CopyMN(M_loc_perm, dofs, dofs, 0, 0,
bi*dofs, bi*dofs);
}
a.AssembleElementMatrix(el_k+s, M_loc_perm, vdofs);
M_loc_perm.CopyMN(DM, dofs, dofs, 0, 0, bi*dofs, bi*dofs);
}
a.AssembleElementMatrix(el, M_loc_perm, vdofs);
}
}
}
@@ -583,7 +513,7 @@ public:
const int NE = mesh.GetNE();
for (int el = 0; el < NE; el++)
{
TTensor3<dofs,vdim,1,AutoSIMD<complex_t,1,1> > x_dof, y_dof;
TTensor3<dofs,vdim,1,complex_t> x_dof, y_dof;
solFES.SetElement(el);
solFES.VectorExtract(solVecLayout, x, x_dof.layout, x_dof);
+8 -8
View File
@@ -65,10 +65,10 @@ struct TMassKernel
template <int qpts>
struct f_asm_data { typedef TVector<qpts,complex_t> type; };
template <typename IR, typename coeff_t, typename impl_traits_t>
template <typename IR, typename coeff_t, int NE>
struct CoefficientEval
{
typedef typename IntRuleCoefficient<IR,coeff_t,impl_traits_t>::Type Type;
typedef typename IntRuleCoefficient<IR,coeff_t,NE>::Type Type;
};
// Method used for un-assembled (matrix free) action.
@@ -180,10 +180,10 @@ struct TDiffusionKernel<1,1,complex_t>
template <int qpts>
struct f_asm_data { typedef TTensor3<qpts,1,1,complex_t> type; };
template <typename IR, typename coeff_t, typename impl_traits_t>
template <typename IR, typename coeff_t, int NE>
struct CoefficientEval
{
typedef typename IntRuleCoefficient<IR,coeff_t,impl_traits_t>::Type Type;
typedef typename IntRuleCoefficient<IR,coeff_t,NE>::Type Type;
};
// Method used for un-assembled (matrix free) action.
@@ -293,10 +293,10 @@ struct TDiffusionKernel<2,2,complex_t>
template <int qpts>
struct f_asm_data { typedef TTensor3<qpts,2,2,complex_t> type; };
template <typename IR, typename coeff_t, typename impl_traits_t>
template <typename IR, typename coeff_t, int NE>
struct CoefficientEval
{
typedef typename IntRuleCoefficient<IR,coeff_t,impl_traits_t>::Type Type;
typedef typename IntRuleCoefficient<IR,coeff_t,NE>::Type Type;
};
// Method used for un-assembled (matrix free) action.
@@ -434,10 +434,10 @@ struct TDiffusionKernel<3,3,complex_t>
template <int qpts>
struct f_asm_data { typedef TTensor3<qpts,3,3,complex_t> type; };
template <typename IR, typename coeff_t, typename impl_traits_t>
template <typename IR, typename coeff_t, int NE>
struct CoefficientEval
{
typedef typename IntRuleCoefficient<IR,coeff_t,impl_traits_t>::Type Type;
typedef typename IntRuleCoefficient<IR,coeff_t,NE>::Type Type;
};
// Method used for un-assembled (matrix free) action.
+8 -29
View File
@@ -81,15 +81,11 @@ protected:
{
const int qpts = T_result_t::x_type::layout_type::dim_1;
const int ne = T_result_t::x_type::layout_type::dim_3;
const int vs = sizeof(T.x[0])/sizeof(T.x[0][0]);
for (int k = 0; k < ne; k++)
{
for (int i = 0; i < qpts; i++)
{
for (int s = 0; s < vs; s++)
{
c[l.ind(i,k)][s] = F.Eval1D(T.x(i,0,k)[s]);
}
c[l.ind(i,k)] = F.Eval1D(T.x(i,0,k));
}
}
}
@@ -102,15 +98,11 @@ protected:
{
const int qpts = T_result_t::x_type::layout_type::dim_1;
const int ne = T_result_t::x_type::layout_type::dim_3;
const int vs = sizeof(T.x[0])/sizeof(T.x[0][0]);
for (int k = 0; k < ne; k++)
{
for (int i = 0; i < qpts; i++)
{
for (int s = 0; s < vs; s++)
{
c[l.ind(i,k)][s] = F.Eval2D(T.x(i,0,k)[s], T.x(i,1,k)[s]);
}
c[l.ind(i,k)] = F.Eval2D(T.x(i,0,k), T.x(i,1,k));
}
}
}
@@ -123,16 +115,11 @@ protected:
{
const int qpts = T_result_t::x_type::layout_type::dim_1;
const int ne = T_result_t::x_type::layout_type::dim_3;
const int vs = sizeof(T.x[0])/sizeof(T.x[0][0]);
for (int k = 0; k < ne; k++)
{
for (int i = 0; i < qpts; i++)
{
for (int s = 0; s < vs; s++)
{
c[l.ind(i,k)][s] =
F.Eval3D(T.x(i,0,k)[s], T.x(i,1,k)[s], T.x(i,2,k)[s]);
}
c[l.ind(i,k)] = F.Eval3D(T.x(i,0,k), T.x(i,1,k), T.x(i,2,k));
}
}
}
@@ -183,16 +170,9 @@ public:
void Eval(const T_result_t &T, const c_layout_t &l, c_data_t &c)
{
const int ne = T_result_t::ne;
const int vs = sizeof(T.attrib[0])/sizeof(T.attrib[0][0]);
MFEM_STATIC_ASSERT(vs == sizeof(c[0])/sizeof(c[0][0]), "");
for (int i = 0; i < ne; i++)
{
typename c_data_t::data_type ci;
for (int s = 0; s < vs; s++)
{
ci[s] = constants(T.attrib[i][s]-1);
}
TAssign<AssignOp::Set>(l.ind2(i), c, ci);
TAssign<AssignOp::Set>(l.ind2(i), c, constants(T.attrib[i]-1));
}
}
};
@@ -263,13 +243,12 @@ public:
/// Auxiliary class that is used to simplify the evaluation of a coefficient and
/// scaling it by the weights of a quadrature rule.
template <typename IR, typename coeff_t, typename impl_traits_t>
template <typename IR, typename coeff_t, int NE>
struct IntRuleCoefficient
{
static const int qpts = IR::qpts;
static const int ne = impl_traits_t::batch_size;
static const int ne = NE;
typedef typename coeff_t::complex_type complex_type;
typedef typename impl_traits_t::vcomplex_t vcomplex_t;
template <bool is_const, bool dummy> struct Aux;
@@ -298,7 +277,7 @@ struct IntRuleCoefficient
// non-constant coefficient
template <bool dummy> struct Aux<false,dummy>
{
typedef TMatrix<qpts,ne,vcomplex_t> result_t;
typedef TMatrix<qpts,ne,complex_type> result_t;
#ifdef MFEM_TEMPLATE_INTRULE_COEFF_PRECOMP
TMatrix<qpts,1,typename IR::real_type> w;
#else
@@ -333,7 +312,7 @@ struct IntRuleCoefficient
}
inline MFEM_ALWAYS_INLINE
const vcomplex_t &get(const result_t &res, int i, int k) const
const complex_type &get(const result_t &res, int i, int k) const
{
return res(i,k);
}
+61 -96
View File
@@ -64,9 +64,9 @@ public:
// Templated struct Result, used to specify the type result that is computed
// by the TElementTransformation::Eval() method and stored in this structure.
// The template parameter EvalOps is a sum (bitwise or) of constants from
// the enum EvalOperations. The type impl_traits_t specifies additional
// parameters and types to be used by the Eval() method.
template<int EvalOps, typename impl_traits_t> struct Result;
// the enum EvalOperations. The parameter NE is the number of elements to be
// processed in the Eval() method.
template<int EvalOps, int NE> struct Result;
static const int dim = Mesh_t::dim;
static const int sdim = Mesh_t::space_dim;
@@ -85,17 +85,13 @@ protected:
const Element* const *elements;
template <typename vint_t, int NE>
template <int NE>
inline MFEM_ALWAYS_INLINE
void SetAttributes(int el, vint_t (&attrib)[NE]) const
void SetAttributes(int el, int (&attrib)[NE]) const
{
const int vsize = sizeof(vint_t)/sizeof(attrib[0][0]);
for (int i = 0; i < NE; i++)
{
for (int j = 0; j < vsize; i++)
{
attrib[i][j] = elements[el+j+i*vsize]->GetAttribute();
}
attrib[i] = elements[el+i]->GetAttribute();
}
}
@@ -110,30 +106,25 @@ public:
{ }
// Evaluate coordinates and/or Jacobian matrices at quadrature points.
template<int EvalOps, typename impl_traits_t>
template<int EvalOps, int NE>
inline MFEM_ALWAYS_INLINE
void Eval(int el, Result<EvalOps,impl_traits_t> &F)
void Eval(int el, Result<EvalOps,NE> &F)
{
F.Eval(el, *this);
}
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
template<int EvalOps, typename impl_traits_t>
template<int EvalOps, int NE>
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, const typename impl_traits_t::vreal_t *nodeData,
Result<EvalOps,impl_traits_t> &F)
void EvalSerialized(int el, const real_t *nodeData, Result<EvalOps,NE> &F)
{
F.EvalSerialized(el, *this, nodeData);
}
#endif
// Specialization of the Result<> class
// Case EvalOps = 0 = EvalNone
template <typename it_t> struct Result<0,it_t>
template <int NE> struct Result<0,NE> // 0 = EvalNone
{
static const int ne = it_t::batch_size;
typedef typename it_t::vreal_t vreal_t;
static const int ne = NE;
// x_type x;
// Jt_type Jt;
// int attrib[NE];
@@ -146,23 +137,20 @@ public:
}
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { }
void EvalSerialized(int el, T_type &T, const real_t *nodeData) { }
#endif
};
// Case EvalOps = 1 = EvalCoordinates
template <typename it_t> struct Result<1,it_t>
template <int NE> struct Result<1,NE> // 1 = EvalCoordinates
{
static const int ne = it_t::batch_size;
typedef typename it_t::vreal_t vreal_t;
static const int ne = NE;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
typedef TTensor3<qpts,sdim,NE,vreal_t,true> x_type;
typedef TTensor3<qpts,sdim,NE,real_t,true> x_type;
#else
typedef TTensor3<qpts,sdim,ne,vreal_t/*,true*/> x_type;
typedef TTensor3<qpts,sdim,NE,real_t/*,true*/> x_type;
#endif
x_type x;
typedef TTensor3<dofs,sdim,ne,vreal_t> nodes_dof_t;
typedef TTensor3<dofs,sdim,NE,real_t> nodes_dof_t;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
nodes_dof_t nodes_dof;
#endif
@@ -171,8 +159,8 @@ public:
void Eval(int el, T_type &T)
{
#ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported");
TTensor3<dofs,sdim,1,vreal_t> &nodes_dof = T.nodes_dof;
MFEM_STATIC_ASSERT(NE == 1, "only NE == 1 is supported");
TTensor3<dofs,sdim,1,real_t> &nodes_dof = T.nodes_dof;
#elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES)
nodes_dof_t nodes_dof;
#endif
@@ -185,30 +173,25 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, T_type &T, const vreal_t *nodeData)
void EvalSerialized(int el, T_type &T, const real_t *nodeData)
{
const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]);
MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el);
T.evaluator.Calc(nodes_dof_t::layout.merge_23(),
&nodeData[el/SS*nodes_dof_t::size],
&nodeData[el*nodes_dof_t::size],
x.layout.merge_23(), x);
}
#endif
};
// Case EvalOps = 2 = EvalJacobians
template <typename it_t> struct Result<2,it_t>
template <int NE> struct Result<2,NE> // 2 = EvalJacobians
{
static const int ne = it_t::batch_size;
typedef typename it_t::vreal_t vreal_t;
static const int ne = NE;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
typedef TTensor4<qpts,dim,sdim,ne,vreal_t,true> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t,true> Jt_type;
#else
typedef TTensor4<qpts,dim,sdim,ne,vreal_t/*,true*/> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t/*,true*/> Jt_type;
#endif
Jt_type Jt;
typedef TTensor3<dofs,sdim,ne,vreal_t> nodes_dof_t;
typedef TTensor3<dofs,sdim,NE,real_t> nodes_dof_t;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
nodes_dof_t nodes_dof;
#endif
@@ -217,8 +200,8 @@ public:
void Eval(int el, T_type &T)
{
#ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported");
TTensor3<dofs,sdim,1,vreal_t> &nodes_dof = T.nodes_dof;
MFEM_STATIC_ASSERT(NE == 1, "only NE == 1 is supported");
TTensor3<dofs,sdim,1,real_t> &nodes_dof = T.nodes_dof;
#elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES)
nodes_dof_t nodes_dof;
#endif
@@ -231,32 +214,27 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, T_type &T, const vreal_t *nodeData)
void EvalSerialized(int el, T_type &T, const real_t *nodeData)
{
const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]);
MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el);
T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(),
&nodeData[el/SS*nodes_dof_t::size],
&nodeData[el*nodes_dof_t::size],
Jt.layout.merge_34(), Jt);
}
#endif
};
// Case EvalOps = 3 = EvalCoordinates|EvalJacobians
template <typename it_t> struct Result<3,it_t>
template <int NE> struct Result<3,NE> // 3 = EvalCoordinates|EvalJacobians
{
static const int ne = it_t::batch_size;
typedef typename it_t::vreal_t vreal_t;
typedef TTensor3<qpts,sdim,ne,vreal_t,true> x_type;
static const int ne = NE;
typedef TTensor3<qpts,sdim,NE,real_t,true> x_type;
x_type x;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
typedef TTensor4<qpts,dim,sdim,ne,vreal_t,true> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t,true> Jt_type;
#else
typedef TTensor4<qpts,dim,sdim,ne,vreal_t/*,true*/> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t/*,true*/> Jt_type;
#endif
Jt_type Jt;
typedef TTensor3<dofs,sdim,ne,vreal_t> nodes_dof_t;
typedef TTensor3<dofs,sdim,NE,real_t> nodes_dof_t;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
nodes_dof_t nodes_dof;
#endif
@@ -265,8 +243,8 @@ public:
void Eval(int el, T_type &T)
{
#ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported");
TTensor3<dofs,sdim,1,vreal_t> &nodes_dof = T.nodes_dof;
MFEM_STATIC_ASSERT(NE == 1, "only NE == 1 is supported");
TTensor3<dofs,sdim,1,real_t> &nodes_dof = T.nodes_dof;
#elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES)
nodes_dof_t nodes_dof;
#endif
@@ -281,45 +259,39 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, T_type &T, const vreal_t *nodeData)
void EvalSerialized(int el, T_type &T, const real_t *nodeData)
{
const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]);
MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el);
T.evaluator.Calc(nodes_dof_t::layout.merge_23(),
&nodeData[el/SS*nodes_dof_t::size],
&nodeData[el*nodes_dof_t::size],
x.layout.merge_23(), x);
T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(),
&nodeData[el/SS*nodes_dof_t::size],
&nodeData[el*nodes_dof_t::size],
Jt.layout.merge_34(), Jt);
}
#endif
};
// Case EvalOps = 6 = EvalJacobians|LoadAttributes
template <typename it_t> struct Result<6,it_t>
template <int NE> struct Result<6,NE> // 6 = EvalJacobians|LoadAttributes
{
static const int ne = it_t::batch_size;
typedef typename it_t::vreal_t vreal_t;
typedef typename it_t::vint_t vint_t;
static const int ne = NE;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
typedef TTensor4<qpts,dim,sdim,ne,vreal_t,true> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t,true> Jt_type;
#else
typedef TTensor4<qpts,dim,sdim,ne,vreal_t/*,true*/> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t/*,true*/> Jt_type;
#endif
Jt_type Jt;
typedef TTensor3<dofs,sdim,ne,vreal_t> nodes_dof_t;
typedef TTensor3<dofs,sdim,NE,real_t> nodes_dof_t;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
nodes_dof_t nodes_dof;
#endif
vint_t attrib[ne];
int attrib[NE];
inline MFEM_ALWAYS_INLINE
void Eval(int el, T_type &T)
{
#ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported");
TTensor3<dofs,sdim,1,vreal_t> &nodes_dof = T.nodes_dof;
MFEM_STATIC_ASSERT(NE == 1, "only NE == 1 is supported");
TTensor3<dofs,sdim,1,real_t> &nodes_dof = T.nodes_dof;
#elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES)
nodes_dof_t nodes_dof;
#endif
@@ -333,31 +305,26 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, T_type &T, const vreal_t *nodeData)
void EvalSerialized(int el, T_type &T, const real_t *nodeData)
{
const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]);
MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el);
T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(),
&nodeData[el/SS*nodes_dof_t::size],
&nodeData[el*nodes_dof_t::size],
Jt.layout.merge_34(), Jt);
T.SetAttributes(el, attrib);
}
#endif
};
// Case EvalOps = 10 = EvalJacobians|LoadElementIdxs
template <typename it_t> struct Result<10,it_t>
template <int NE> struct Result<10,NE> // 10 = EvalJacobians|LoadElementIdxs
{
static const int ne = it_t::batch_size;
typedef typename it_t::vreal_t vreal_t;
static const int ne = NE;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
typedef TTensor4<qpts,dim,sdim,ne,vreal_t,true> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t,true> Jt_type;
#else
typedef TTensor4<qpts,dim,sdim,ne,vreal_t/*,true*/> Jt_type;
typedef TTensor4<qpts,dim,sdim,NE,real_t/*,true*/> Jt_type;
#endif
Jt_type Jt;
typedef TTensor3<dofs,sdim,ne,vreal_t> nodes_dof_t;
typedef TTensor3<dofs,sdim,NE,real_t> nodes_dof_t;
#ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES
nodes_dof_t nodes_dof;
#endif
@@ -367,8 +334,8 @@ public:
void Eval(int el, T_type &T)
{
#ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported");
TTensor3<dofs,sdim,1,vreal_t> &nodes_dof = T.nodes_dof;
MFEM_STATIC_ASSERT(NE == 1, "only NE == 1 is supported");
TTensor3<dofs,sdim,1,real_t> &nodes_dof = T.nodes_dof;
#elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES)
nodes_dof_t nodes_dof;
#endif
@@ -382,12 +349,10 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
inline MFEM_ALWAYS_INLINE
void EvalSerialized(int el, T_type &T, const vreal_t *nodeData)
void EvalSerialized(int el, T_type &T, const real_t *nodeData)
{
const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]);
MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el);
T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(),
&nodeData[el/SS*nodes_dof_t::size],
&nodeData[el*nodes_dof_t::size],
Jt.layout.merge_34(), Jt);
first_elem_idx = el;
}
+83 -110
View File
@@ -58,7 +58,7 @@ public:
// dof_layout is (DOF x NumComp) and qpt_layout is (NIP x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename qpt_layout_t, typename qpt_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data,
const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const
{
@@ -81,7 +81,7 @@ public:
template <bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const dof_layout_t &dof_layout, dof_data_t &dof_data) const
{
@@ -103,7 +103,7 @@ public:
// dof_layout is (DOF x NumComp) and grad_layout is (NIP x DIM x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename grad_layout_t, typename grad_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGrad(const dof_layout_t &dof_layout,
const dof_data_t &dof_data,
const grad_layout_t &grad_layout,
@@ -129,7 +129,7 @@ public:
template <bool Add,
typename grad_layout_t, typename grad_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGradT(const grad_layout_t &grad_layout,
const grad_data_t &grad_data,
const dof_layout_t &dof_layout,
@@ -154,7 +154,7 @@ public:
// qpt_layout is (NIP x NumComp), M_layout is (DOF x DOF x NumComp)
template <typename qpt_layout_t, typename qpt_data_t,
typename M_layout_t, typename M_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const M_layout_t &M_layout, M_data_t &M_data) const
{
@@ -178,15 +178,14 @@ public:
// D_layout is (DOF x DOF x NumComp).
template <typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void AssembleGradGrad(const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
const D_layout_t &D_layout,
D_data_t &D_data) const
{
const int NC = qpt_layout_t::dim_4;
typedef typename qpt_data_t::data_type entry_type;
TTensor4<NIP,DIM,DOF,NC,entry_type> F;
TTensor4<NIP,DIM,DOF,NC> F;
for (int k = 0; k < NC; k++)
{
// Next loop performs a batch of matrix-matrix products of size
@@ -225,7 +224,7 @@ public:
// dof_layout is (DOF x NumComp) and qpt_layout is (NIP x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename qpt_layout_t, typename qpt_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data,
const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const
{
@@ -239,7 +238,7 @@ public:
template <bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const dof_layout_t &dof_layout, dof_data_t &dof_data) const
{
@@ -252,7 +251,7 @@ public:
// dof_layout is (DOF x NumComp) and grad_layout is (NIP x DIM x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename grad_layout_t, typename grad_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGrad(const dof_layout_t &dof_layout,
const dof_data_t &dof_data,
const grad_layout_t &grad_layout,
@@ -269,7 +268,7 @@ public:
template <bool Add,
typename grad_layout_t, typename grad_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGradT(const grad_layout_t &grad_layout,
const grad_data_t &grad_data,
const dof_layout_t &dof_layout,
@@ -286,7 +285,7 @@ public:
// qpt_layout is (NIP x NumComp), M_layout is (DOF x DOF x NumComp)
template <typename qpt_layout_t, typename qpt_data_t,
typename M_layout_t, typename M_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const M_layout_t &M_layout, M_data_t &M_data) const
{
@@ -310,7 +309,7 @@ public:
// D_layout is (DOF x DOF x NumComp).
template <typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void AssembleGradGrad(const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
const D_layout_t &D_layout,
@@ -349,14 +348,13 @@ public:
template <bool Dx, bool Dy,
typename dof_layout_t, typename dof_data_t,
typename qpt_layout_t, typename qpt_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data,
const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const
{
const int NC = dof_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
// DOF x DOF x NC --> NIP x DOF x NC --> NIP x NIP x NC
TTensor3<NIP,DOF,NC,entry_type> A;
TTensor3<NIP,DOF,NC> A;
// (1) A_{i,j,k} = \sum_s B_1d_{i,s} dof_data_{s,j,k}
Mult_2_1<false>(B_1d.layout, Dx ? G_1d : B_1d,
@@ -372,7 +370,7 @@ public:
// dof_layout is (TDOF x NumComp) and qpt_layout is (TNIP x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename qpt_layout_t, typename qpt_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data,
const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const
{
@@ -382,14 +380,13 @@ public:
template <bool Dx, bool Dy, bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const dof_layout_t &dof_layout, dof_data_t &dof_data) const
{
const int NC = dof_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
// NIP x NIP X NC --> NIP x DOF x NC --> DOF x DOF x NC
TTensor3<NIP,DOF,NC,entry_type> A;
TTensor3<NIP,DOF,NC> A;
// (1) A_{i,j,k} = \sum_s B_1d_{s,j} qpt_data_{i,s,k}
Mult_1_2<false>(B_1d.layout, Dy ? G_1d : B_1d,
@@ -406,7 +403,7 @@ public:
template <bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const dof_layout_t &dof_layout, dof_data_t &dof_data) const
{
@@ -417,7 +414,7 @@ public:
// dof_layout is (TDOF x NumComp) and grad_layout is (TNIP x DIM x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename grad_layout_t, typename grad_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGrad(const dof_layout_t &dof_layout,
const dof_data_t &dof_data,
const grad_layout_t &grad_layout,
@@ -435,7 +432,7 @@ public:
template <bool Add,
typename grad_layout_t, typename grad_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGradT(const grad_layout_t &grad_layout,
const grad_data_t &grad_data,
const dof_layout_t &dof_layout,
@@ -451,12 +448,11 @@ public:
// qpt_layout is (TNIP x NumComp), M_layout is (TDOF x TDOF x NumComp)
template <typename qpt_layout_t, typename qpt_data_t,
typename M_layout_t, typename M_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const M_layout_t &M_layout, M_data_t &M_data) const
{
const int NC = qpt_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
// Using TensorAssemble: <I,NIP,J> --> <DOF,I,DOF,J>
@@ -473,7 +469,7 @@ public:
TTensor3<DOF,NIP,DOF*NC>::layout, A,
M_layout.merge_23().template split_12<DOF,DOF,DOF,DOF*NC>(), M_data);
#elif 1
TTensor4<DOF,NIP,DOF,NC,entry_type> A;
TTensor4<DOF,NIP,DOF,NC> A;
// qpt_data<NIP1,NIP2,NC> --> A<DOF2,NIP1,DOF2,NC>
TensorAssemble<false>(
Bt_1d.layout, Bt_1d, B_1d.layout, B_1d,
@@ -514,15 +510,14 @@ public:
template <int D1, int D2, bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
const D_layout_t &D_layout,
D_data_t &D_data) const
{
const int NC = qpt_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
TTensor4<DOF,NIP,DOF,NC,entry_type> A;
TTensor4<DOF,NIP,DOF,NC> A;
// Using TensorAssemble: <I,NIP,J> --> <DOF,I,DOF,J>
@@ -536,7 +531,7 @@ public:
TensorAssemble<Add>(
Bt_1d.layout, D1 == 1 ? Bt_1d : Gt_1d,
B_1d.layout, D2 == 1 ? B_1d : G_1d,
A.layout.merge_34(), A,
TTensor3<DOF,NIP,DOF*NC>::layout, A,
D_layout.merge_23().template split_12<DOF,DOF,DOF,DOF*NC>(), D_data);
}
@@ -545,7 +540,7 @@ public:
// D_layout is (TDOF x TDOF x NumComp).
template <typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void AssembleGradGrad(const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
const D_layout_t &D_layout,
@@ -629,14 +624,13 @@ public:
template <bool Dx, bool Dy, bool Dz,
typename dof_layout_t, typename dof_data_t,
typename qpt_layout_t, typename qpt_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data,
const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const
{
const int NC = dof_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
TVector<NIP*DOF*DOF*NC,entry_type> QDD;
TVector<NIP*NIP*DOF*NC,entry_type> QQD;
TVector<NIP*DOF*DOF*NC> QDD;
TVector<NIP*NIP*DOF*NC> QQD;
// QDD_{i,jj,k} = \sum_s B_1d_{i,s} dof_data_{s,jj,k}
Mult_2_1<false>(B_1d.layout, Dx ? G_1d : B_1d,
@@ -656,7 +650,7 @@ public:
// dof_layout is (TDOF x NumComp) and qpt_layout is (TNIP x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename qpt_layout_t, typename qpt_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data,
const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const
{
@@ -666,14 +660,13 @@ public:
template <bool Dx, bool Dy, bool Dz, bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const dof_layout_t &dof_layout, dof_data_t &dof_data) const
{
const int NC = dof_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
TVector<NIP*DOF*DOF*NC,entry_type> QDD;
TVector<NIP*NIP*DOF*NC,entry_type> QQD;
TVector<NIP*DOF*DOF*NC> QDD;
TVector<NIP*NIP*DOF*NC> QQD;
// QQD_{ii,j,k} = \sum_s B_1d_{s,j} qpt_data_{ii,s,k}
Mult_1_2<false>(B_1d.layout, Dz ? G_1d : B_1d,
@@ -694,7 +687,7 @@ public:
template <bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const dof_layout_t &dof_layout, dof_data_t &dof_data) const
{
@@ -705,7 +698,7 @@ public:
// dof_layout is (TDOF x NumComp) and grad_layout is (TNIP x DIM x NumComp).
template <typename dof_layout_t, typename dof_data_t,
typename grad_layout_t, typename grad_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGrad(const dof_layout_t &dof_layout,
const dof_data_t &dof_data,
const grad_layout_t &grad_layout,
@@ -727,7 +720,7 @@ public:
template <bool Add,
typename grad_layout_t, typename grad_data_t,
typename dof_layout_t, typename dof_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void CalcGradT(const grad_layout_t &grad_layout,
const grad_data_t &grad_data,
const dof_layout_t &dof_layout,
@@ -745,14 +738,13 @@ public:
// qpt_layout is (TNIP x NumComp), M_layout is (TDOF x TDOF x NumComp)
template <typename qpt_layout_t, typename qpt_data_t,
typename M_layout_t, typename M_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data,
const M_layout_t &M_layout, M_data_t &M_data) const
{
const int NC = qpt_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
TTensor4<DOF,NIP*NIP,DOF,NC,entry_type> A1;
TTensor4<DOF,DOF*NIP,DOF,DOF*NC,entry_type> A2;
TTensor4<DOF,NIP*NIP,DOF,NC> A1;
TTensor4<DOF,DOF*NIP,DOF,DOF*NC> A2;
// Using TensorAssemble: <I,NIP,J> --> <DOF,I,DOF,J>
@@ -796,16 +788,15 @@ public:
template <int D1, int D2, bool Add,
typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
const D_layout_t &D_layout,
D_data_t &D_data) const
{
const int NC = qpt_layout_t::dim_2;
typedef typename qpt_data_t::data_type entry_type;
TTensor4<DOF,NIP*NIP,DOF,NC,entry_type> A1;
TTensor4<DOF,DOF*NIP,DOF,DOF*NC,entry_type> A2;
TTensor4<DOF,NIP*NIP,DOF,NC> A1;
TTensor4<DOF,DOF*NIP,DOF,DOF*NC> A2;
// Using TensorAssemble: <I,NIP,J> --> <DOF,I,DOF,J>
@@ -833,7 +824,7 @@ public:
#if 0
template <typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void Assemble(int D1, int D2,
const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
@@ -873,7 +864,7 @@ public:
// D_layout is (TDOF x TDOF x NumComp).
template <typename qpt_layout_t, typename qpt_data_t,
typename D_layout_t, typename D_data_t>
inline MFEM_ALWAYS_INLINE
MFEM_ALWAYS_INLINE
void AssembleGradGrad(const qpt_layout_t &qpt_layout,
const qpt_data_t &qpt_data,
const D_layout_t &D_layout,
@@ -1064,7 +1055,7 @@ public:
void GetValues(int el, const val_layout_t &l, val_data_t &vals)
{
const int ne = val_layout_t::dim_3;
TTensor3<dofs,vdim,ne,typename val_data_t::data_type> val_dofs;
TTensor3<dofs,vdim,ne,complex_type> val_dofs;
SetElement(el);
fespace.VectorExtract(vec_layout, data_in, val_dofs.layout, val_dofs);
shapeEval.Calc(val_dofs.layout.merge_23(), val_dofs, l.merge_23(), vals);
@@ -1076,7 +1067,7 @@ public:
void GetGradients(int el, const grad_layout_t &l, grad_data_t &grad)
{
const int ne = grad_layout_t::dim_4;
TTensor3<dofs,vdim,ne,typename grad_data_t::data_type> val_dofs;
TTensor3<dofs,vdim,ne,complex_type> val_dofs;
SetElement(el);
fespace.VectorExtract(vec_layout, data_in, val_dofs.layout, val_dofs);
shapeEval.CalcGrad(val_dofs.layout.merge_23(), val_dofs,
@@ -1121,16 +1112,14 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
template <typename DataType>
inline MFEM_ALWAYS_INLINE
void EvalSerialized(const typename DataType::vcomplex_t *loc_dofs,
DataType &F)
void EvalSerialized(const complex_t *loc_dofs, DataType &F)
{
Action<DataType::InData,true>::EvalSerialized(*this, loc_dofs, F);
}
template <bool Add, typename DataType>
inline MFEM_ALWAYS_INLINE
void AssembleSerialized(const DataType &F,
typename DataType::vcomplex_t *loc_dofs)
void AssembleSerialized(const DataType &F, complex_t *loc_dofs)
{
Action<DataType::OutData,true>::
template AssembleSerialized<Add>(*this, F, loc_dofs);
@@ -1149,61 +1138,56 @@ public:
// Auxiliary templated struct AData, used by the Eval() and Assemble()
// methods. The template parameter IOData is "bitwise or" of constants from
// the enum InOutData. The type impl_traits_t specifies parameters and types
// to be used in the Eval() and Assemble() methods.
template<int IOData, typename impl_traits_t> struct AData;
// the enum InOutData. The parameter NE is the number of elements to be
// processed in the Eval() and Assemble() methods.
template<int IOData, int NE> struct AData;
template <typename it_t> struct AData<0,it_t> // 0 = None
template <int NE> struct AData<0,NE> // 0 = None
{
// Do we need this?
};
template <typename it_t> struct AData<1,it_t> // 1 = Values
template <int NE> struct AData<1,NE> // 1 = Values
{
static const int ne = it_t::batch_size;
typedef typename it_t::vcomplex_t vcomplex_t;
#ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS
typedef TTensor3<dofs,vdim,ne,vcomplex_t,true> val_dofs_t;
typedef TTensor3<dofs,vdim,NE,complex_t,true> val_dofs_t;
val_dofs_t val_dofs;
#else
typedef TTensor3<dofs,vdim,ne,vcomplex_t> val_dofs_t;
typedef TTensor3<dofs,vdim,NE,complex_t> val_dofs_t;
#endif
TTensor3<qpts,vdim,ne,vcomplex_t> val_qpts;
TTensor3<qpts,vdim,NE,complex_t> val_qpts;
};
template <typename it_t> struct AData<2,it_t> // 2 = Gradients
template <int NE> struct AData<2,NE> // 2 = Gradients
{
static const int ne = it_t::batch_size;
typedef typename it_t::vcomplex_t vcomplex_t;
#ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS
typedef TTensor3<dofs,vdim,ne,vcomplex_t,true> val_dofs_t;
typedef TTensor3<dofs,vdim,NE,complex_t,true> val_dofs_t;
val_dofs_t val_dofs;
#else
typedef TTensor3<dofs,vdim,ne,vcomplex_t> val_dofs_t;
typedef TTensor3<dofs,vdim,NE,complex_t> val_dofs_t;
#endif
TTensor4<qpts,dim,vdim,ne,vcomplex_t> grad_qpts;
TTensor4<qpts,dim,vdim,NE,complex_t> grad_qpts;
};
template <typename it_t> struct AData<3,it_t> // 3 = Values+Gradients
template <int NE> struct AData<3,NE> // 3 = Values+Gradients
{
static const int ne = it_t::batch_size;
typedef typename it_t::vcomplex_t vcomplex_t;
#ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS
typedef TTensor3<dofs,vdim,ne,vcomplex_t,true> val_dofs_t;
typedef TTensor3<dofs,vdim,NE,complex_t,true> val_dofs_t;
val_dofs_t val_dofs;
#else
typedef TTensor3<dofs,vdim,ne,vcomplex_t> val_dofs_t;
typedef TTensor3<dofs,vdim,NE,complex_t> val_dofs_t;
#endif
TTensor3<qpts, vdim,ne,vcomplex_t,true> val_qpts;
TTensor4<qpts,dim,vdim,ne,vcomplex_t> grad_qpts;
TTensor3<qpts, vdim,NE,complex_t,true> val_qpts;
TTensor4<qpts,dim,vdim,NE,complex_t> grad_qpts;
};
// This struct is similar to struct AData, adding separate static data
// members for the input (InData) and output (OutData) data types.
template <int IData, int OData, typename it_t>
struct BData : public AData<IData|OData,it_t>
template <int IData, int OData, int NE>
struct BData : public AData<IData|OData,NE>
{
typedef T_type eval_type;
static const int ne = NE;
static const int InData = IData;
static const int OutData = OData;
};
@@ -1254,9 +1238,7 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
template <typename AData_t>
static inline MFEM_ALWAYS_INLINE
void EvalSerialized(T_type &T,
const typename AData_t::vcomplex_t *loc_dofs,
AData_t &D)
void EvalSerialized(T_type &T, const complex_t *loc_dofs, AData_t &D)
{
T.shapeEval.Calc(AData_t::val_dofs_t::layout.merge_23(), loc_dofs,
D.val_qpts.layout.merge_23(), D.val_qpts);
@@ -1264,8 +1246,7 @@ public:
template <bool Add, typename AData_t>
static inline MFEM_ALWAYS_INLINE
void AssembleSerialized(T_type &T, const AData_t &D,
typename AData_t::vcomplex_t *loc_dofs)
void AssembleSerialized(T_type &T, const AData_t &D, complex_t *loc_dofs)
{
T.shapeEval.template CalcT<Add>(
D.val_qpts.layout.merge_23(), D.val_qpts,
@@ -1310,9 +1291,7 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
template <typename AData_t>
static inline MFEM_ALWAYS_INLINE
void EvalSerialized(T_type &T,
const typename AData_t::vcomplex_t *loc_dofs,
AData_t &D)
void EvalSerialized(T_type &T, const complex_t *loc_dofs, AData_t &D)
{
T.shapeEval.CalcGrad(AData_t::val_dofs_t::layout.merge_23(), loc_dofs,
D.grad_qpts.layout.merge_34(), D.grad_qpts);
@@ -1320,8 +1299,7 @@ public:
template <bool Add, typename AData_t>
static inline MFEM_ALWAYS_INLINE
void AssembleSerialized(T_type &T, const AData_t &D,
typename AData_t::vcomplex_t *loc_dofs)
void AssembleSerialized(T_type &T, const AData_t &D, complex_t *loc_dofs)
{
T.shapeEval.template CalcGradT<Add>(
D.grad_qpts.layout.merge_34(), D.grad_qpts,
@@ -1371,9 +1349,7 @@ public:
#ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE
template <typename AData_t>
static inline MFEM_ALWAYS_INLINE
void EvalSerialized(T_type &T,
const typename AData_t::vcomplex_t *loc_dofs,
AData_t &D)
void EvalSerialized(T_type &T, const complex_t *loc_dofs, AData_t &D)
{
T.shapeEval.Calc(AData_t::val_dofs_t::layout.merge_23(), loc_dofs,
D.val_qpts.layout.merge_23(), D.val_qpts);
@@ -1383,8 +1359,7 @@ public:
template <bool Add, typename AData_t>
static inline MFEM_ALWAYS_INLINE
void AssembleSerialized(T_type &T, const AData_t &D,
typename AData_t::vcomplex_t *loc_dofs)
void AssembleSerialized(T_type &T, const AData_t &D, complex_t *loc_dofs)
{
T.shapeEval.template CalcT<Add>(
D.val_qpts.layout.merge_23(), D.val_qpts,
@@ -1398,13 +1373,12 @@ public:
// This struct implements element matrix computation for some combinations
// of input (InOps) and output (OutOps) operations.
template <int InOps, int OutOps, typename it_t> struct TElementMatrix;
template <int InOps, int OutOps, int NE> struct TElementMatrix;
// Case 1,1 = Values,Values
template <typename it_t> struct TElementMatrix<1,1,it_t>
template <int NE> struct TElementMatrix<1,1,NE> // 1,1 = Values,Values
{
// qpt_layout_t is (nip), M_layout_t is (dof x dof)
// it_t::batch_size = 1 is assumed
// NE = 1 is assumed
template <typename qpt_layout_t, typename qpt_data_t,
typename M_layout_t, typename M_data_t>
static inline MFEM_ALWAYS_INLINE
@@ -1416,11 +1390,10 @@ public:
}
};
// Case 2,2 = Gradients,Gradients
template <typename it_t> struct TElementMatrix<2,2,it_t>
template <int NE> struct TElementMatrix<2,2,NE> // 2,2 = Gradients,Gradients
{
// qpt_layout_t is (nip x dim x dim), M_layout_t is (dof x dof)
// it_t::batch_size = 1 is assumed
// NE = 1 is assumed
template <typename qpt_layout_t, typename qpt_data_t,
typename M_layout_t, typename M_data_t>
static inline MFEM_ALWAYS_INLINE
@@ -1432,15 +1405,15 @@ public:
}
};
template <typename kernel_t, typename impl_traits_t> struct Spec
template <typename kernel_t, int NE> struct Spec
{
static const int InData =
Values*kernel_t::in_values + Gradients*kernel_t::in_gradients;
static const int OutData =
Values*kernel_t::out_values + Gradients*kernel_t::out_gradients;
typedef BData<InData,OutData,impl_traits_t> DataType;
typedef TElementMatrix<InData,OutData,impl_traits_t> ElementMatrix;
typedef BData<InData,OutData,NE> DataType;
typedef TElementMatrix<InData,OutData,NE> ElementMatrix;
};
};
+29 -96
View File
@@ -114,23 +114,18 @@ class TFiniteElementSpace_simple
public:
typedef FE FE_type;
typedef IndexType index_type;
static const int dofs = FE::dofs;
protected:
index_type ind;
int num_elems, remain_elems;
public:
TFiniteElementSpace_simple(const FE &fe, const FiniteElementSpace &fes)
: ind(fe, fes), num_elems(fes.GetNE()), remain_elems(num_elems) { }
: ind(fe, fes) { }
// default copy constructor
int GetNE() const { return num_elems; }
void SetElement(int el) { ind.SetElement(el); }
void SetElement(int el) { ind.SetElement(el); remain_elems = num_elems-el; }
#if 0
// Multi-element Extract:
// Extract dofs for multiple elements starting with the current element.
// The number of elements to extract is given by the second dimension of
@@ -142,7 +137,6 @@ public:
const dof_layout_t &dof_layout,
dof_data_t &dof_data) const
{
const int SS = sizeof(dof_data[0])/sizeof(dof_data[0][0]);
const int NE = dof_layout_t::dim_2;
MFEM_STATIC_ASSERT(FE::dofs == dof_layout_t::dim_1,
"invalid number of dofs");
@@ -150,11 +144,8 @@ public:
{
for (int i = 0; i < FE::dofs; i++)
{
for (int s = 0; s < SS; s++)
{
Assign<Op>(dof_data[dof_layout.ind(i,j)][s],
glob_dof_data[ind.map(i,s+SS*j)]);
}
Assign<Op>(dof_data[dof_layout.ind(i,j)],
glob_dof_data[ind.map(i,j)]);
}
}
}
@@ -178,7 +169,6 @@ public:
const dof_data_t &dof_data,
glob_dof_data_t &glob_dof_data) const
{
const int SS = sizeof(dof_data[0])/sizeof(dof_data[0][0]);
const int NE = dof_layout_t::dim_2;
MFEM_STATIC_ASSERT(FE::dofs == dof_layout_t::dim_1,
"invalid number of dofs");
@@ -186,11 +176,8 @@ public:
{
for (int i = 0; i < FE::dofs; i++)
{
for (int s = 0; s < SS; s++)
{
Assign<Op>(glob_dof_data[ind.map(i,s+SS*j)],
dof_data[dof_layout.ind(i,j)][s]);
}
Assign<Op>(glob_dof_data[ind.map(i,j)],
dof_data[dof_layout.ind(i,j)]);
}
}
}
@@ -204,7 +191,6 @@ public:
{
Assemble<AssignOp::Add>(dof_layout, dof_data, glob_dof_data);
}
#endif
// Multi-element VectorExtract: vdof_layout is (DOFS x NumComp x NumElems).
template <AssignOp::Type Op,
@@ -216,39 +202,21 @@ public:
const vdof_layout_t &vdof_layout,
vdof_data_t &vdof_data) const
{
const int SS = sizeof(vdof_data[0])/sizeof(vdof_data[0][0]);
const int NC = vdof_layout_t::dim_2;
const int NE = vdof_layout_t::dim_3;
MFEM_STATIC_ASSERT(FE::dofs == vdof_layout_t::dim_1,
"invalid number of dofs");
MFEM_ASSERT(NC == vl.NumComponents(), "invalid number of components");
const int TE = std::min(SS*NE, remain_elems);
// const int TE = SS*NE;
for (int k = 0; k < NC; k++)
{
#if 0
for (int j = 0; j < NE; j++)
{
for (int i = 0; i < FE::dofs; i++)
{
for (int s = 0; s < SS; s++)
{
Assign<Op>(vdof_data[vdof_layout.ind(i,k,j)][s],
glob_vdof_data[vl.ind(ind.map(i,s+SS*j), k)]);
}
Assign<Op>(vdof_data[vdof_layout.ind(i,k,j)],
glob_vdof_data[vl.ind(ind.map(i,j), k)]);
}
}
#else
for (int js = 0; js < TE; js++)
{
for (int i = 0; i < FE::dofs; i++)
{
const int s = js % SS, j = js / SS;
Assign<Op>(vdof_data[vdof_layout.ind(i,k,j)][s],
glob_vdof_data[vl.ind(ind.map(i,js), k)]);
}
}
#endif
}
}
@@ -273,39 +241,21 @@ public:
const vec_layout_t &vl,
glob_vdof_data_t &glob_vdof_data) const
{
const int SS = sizeof(vdof_data[0])/sizeof(vdof_data[0][0]);
const int NC = vdof_layout_t::dim_2;
const int NE = vdof_layout_t::dim_3;
MFEM_STATIC_ASSERT(FE::dofs == vdof_layout_t::dim_1,
"invalid number of dofs");
MFEM_ASSERT(NC == vl.NumComponents(), "invalid number of components");
const int TE = std::min(SS*NE, remain_elems);
// const int TE = SS*NE;
for (int k = 0; k < NC; k++)
{
#if 0
for (int j = 0; j < NE; j++)
{
for (int i = 0; i < FE::dofs; i++)
{
for (int s = 0; s < SS; s++)
{
Assign<Op>(glob_vdof_data[vl.ind(ind.map(i,s+SS*j), k)],
vdof_data[vdof_layout.ind(i,k,j)][s]);
}
Assign<Op>(glob_vdof_data[vl.ind(ind.map(i,j), k)],
vdof_data[vdof_layout.ind(i,k,j)]);
}
}
#else
for (int js = 0; js < TE; js++)
{
for (int i = 0; i < FE::dofs; i++)
{
const int s = js % SS, j = js / SS;
Assign<Op>(glob_vdof_data[vl.ind(ind.map(i,js), k)],
vdof_data[vdof_layout.ind(i,k,j)][s]);
}
}
#endif
}
}
@@ -332,24 +282,21 @@ public:
const vdof_layout_t &vdof_layout,
vdof_data_t &vdof_data) const
{
const int SS = sizeof(vdof_data[0])/sizeof(vdof_data[0][0]);
const int NC = vdof_layout_t::dim_2;
const int NE = vdof_layout_t::dim_3;
const int TE = std::min(SS*NE, remain_elems);
MFEM_STATIC_ASSERT(FE::dofs == vdof_layout_t::dim_1,
"invalid number of dofs");
MFEM_ASSERT(first_comp + NC <= vl.NumComponents(),
"invalid number of components");
for (int k = 0; k < NC; k++)
{
for (int js = 0; js < TE; js++)
for (int j = 0; j < NE; j++)
{
for (int i = 0; i < FE::dofs; i++)
{
const int s = js % SS, j = js / SS;
Assign<AssignOp::Set>(
vdof_data[vdof_layout.ind(i,k,j)][s],
glob_vdof_data[vl.ind(ind.map(i,js), first_comp+k)]);
vdof_data[vdof_layout.ind(i,k,j)],
glob_vdof_data[vl.ind(ind.map(i,j), first_comp+k)]);
}
}
}
@@ -367,69 +314,55 @@ public:
const vec_layout_t &vl,
glob_vdof_data_t &glob_vdof_data) const
{
const int SS = sizeof(vdof_data[0])/sizeof(vdof_data[0][0]);
const int NC = vdof_layout_t::dim_2;
const int NE = vdof_layout_t::dim_3;
const int TE = std::min(SS*NE, remain_elems);
MFEM_STATIC_ASSERT(FE::dofs == vdof_layout_t::dim_1,
"invalid number of dofs");
MFEM_ASSERT(first_comp + NC <= vl.NumComponents(),
"invalid number of components");
for (int k = 0; k < NC; k++)
{
for (int js = 0; js < TE; js++)
for (int j = 0; j < NE; j++)
{
for (int i = 0; i < FE::dofs; i++)
{
const int s = js % SS, j = js / SS;
Assign<AssignOp::Add>(
glob_vdof_data[vl.ind(ind.map(i,js), first_comp+k)],
vdof_data[vdof_layout.ind(i,k,j)][s]);
glob_vdof_data[vl.ind(ind.map(i,j), first_comp+k)],
vdof_data[vdof_layout.ind(i,k,j)]);
}
}
}
}
template <typename vcomplex_t>
void Assemble(const TMatrix<FE::dofs,FE::dofs,vcomplex_t> &m,
void Assemble(const TMatrix<FE::dofs,FE::dofs,double> &m,
SparseMatrix &M) const
{
const int SS = sizeof(m[0])/sizeof(m[0][0]);
const int TE = std::min(SS, remain_elems);
MFEM_FLOPS_ADD(FE::dofs*FE::dofs);
for (int s = 0; s < TE; s++)
for (int i = 0; i < FE::dofs; i++)
{
for (int i = 0; i < FE::dofs; i++)
M.SetColPtr(ind.map(i,0));
for (int j = 0; j < FE::dofs; j++)
{
M.SetColPtr(ind.map(i,s));
for (int j = 0; j < FE::dofs; j++)
{
M._Add_(ind.map(j,s), m(i,j)[s]);
}
M.ClearColPtr();
M._Add_(ind.map(j,0), m(i,j));
}
M.ClearColPtr();
}
}
template <typename vec_layout_t, typename vcomplex_t>
template <typename vec_layout_t>
void AssembleBlock(int block_i, int block_j, const vec_layout_t &vl,
const TMatrix<FE::dofs,FE::dofs,vcomplex_t> &m,
const TMatrix<FE::dofs,FE::dofs,double> &m,
SparseMatrix &M) const
{
const int SS = sizeof(m[0])/sizeof(m[0][0]);
const int TE = std::min(SS, remain_elems);
MFEM_FLOPS_ADD(FE::dofs*FE::dofs);
for (int s = 0; s < TE; s++)
for (int i = 0; i < FE::dofs; i++)
{
for (int i = 0; i < FE::dofs; i++)
M.SetColPtr(vl.ind(ind.map(i,0), block_i));
for (int j = 0; j < FE::dofs; j++)
{
M.SetColPtr(vl.ind(ind.map(i,s), block_i));
for (int j = 0; j < FE::dofs; j++)
{
M._Add_(vl.ind(ind.map(j,s), block_j), m(i,j)[s]);
}
M.ClearColPtr();
M._Add_(vl.ind(ind.map(j,0), block_j), m(i,j));
}
M.ClearColPtr();
}
}
};
+53 -338
View File
@@ -941,7 +941,6 @@ void AnalyticAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
IsoparametricTransformation Tpr;
Tpr.SetFE(&fe);
Tpr.ElementNo = e_id;
Tpr.ElementType = ElementTransformation::ELEMENT;
Tpr.GetPointMat().Transpose(point_mat);
for (int i = 0; i < ir.GetNPoints(); i++)
@@ -958,176 +957,42 @@ void AnalyticAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
}
#ifdef MFEM_USE_MPI
void DiscreteAdaptTC::FinalizeParDiscreteTargetSpec(const ParGridFunction
&tspec_)
void DiscreteAdaptTC::SetParDiscreteTargetSpec(ParGridFunction &tspec_)
{
MFEM_VERIFY(adapt_eval, "SetAdaptivityEvaluator() has not been called!")
MFEM_VERIFY(ncomp > 0, "No target specifications have been set!");
tspec.SetSize(tspec_.Size());
tspec = tspec_;
tspec_fes = tspec_.FESpace();
ParFiniteElementSpace *ptspec_fes = tspec_.ParFESpace();
if (!adapt_eval) { MFEM_ABORT("Set adaptivity evaluator\n"); }
adapt_eval->SetParMetaInfo(*ptspec_fes->GetParMesh(),
*ptspec_fes->FEColl(), ncomp);
adapt_eval->SetInitialField(*tspec_fes->GetMesh()->GetNodes(), tspec);
adapt_eval->SetParMetaInfo(*tspec_.ParFESpace()->GetParMesh(),
*tspec_.FESpace()->FEColl(),
tspec_.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec_.FESpace()->GetMesh()->GetNodes(), tspec);
tspec_sav = tspec;
delete tspec_fesv;
tspec_fesv = new FiniteElementSpace(tspec_fes->GetMesh(),
tspec_fes->FEColl(), ncomp);
}
void DiscreteAdaptTC::SetTspecAtIndex(int idx, const ParGridFunction &tspec_)
{
const int vdim = tspec_.FESpace()->GetVDim(),
dof_cnt = tspec_.Size()/vdim;
for (int i = 0; i < dof_cnt*vdim; i++)
{
tspec(i+idx*dof_cnt) = tspec_(i);
}
FinalizeParDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetParDiscreteTargetSize(const ParGridFunction &tspec_)
{
if (sizeidx > -1) { SetTspecAtIndex(sizeidx, tspec_); return; }
sizeidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetParDiscreteTargetSkew(const ParGridFunction &tspec_)
{
if (skewidx > -1) { SetTspecAtIndex(skewidx, tspec_); return; }
skewidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetParDiscreteTargetAspectRatio(const ParGridFunction
&tspec_)
{
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, tspec_); return; }
aspectratioidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetParDiscreteTargetOrientation(const ParGridFunction
&tspec_)
{
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, tspec_); return; }
orientationidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
}
void DiscreteAdaptTC::SetParDiscreteTargetSpec(const ParGridFunction &tspec_)
{
SetParDiscreteTargetSize(tspec_);
FinalizeParDiscreteTargetSpec(tspec_);
}
#endif
void DiscreteAdaptTC::SetDiscreteTargetBase(const GridFunction &tspec_)
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(GridFunction &tspec_)
{
const int vdim = tspec_.FESpace()->GetVDim(),
dof_cnt = tspec_.Size()/vdim;
tspec.SetSize(tspec_.Size());
tspec = tspec_;
tspec_fes = tspec_.FESpace();
ncomp += vdim;
if (!adapt_eval) { MFEM_ABORT("Set adaptivity evaluator\n"); }
delete tspec_fes;
tspec_fes = new FiniteElementSpace(tspec_.FESpace()->GetMesh(),
tspec_.FESpace()->FEColl(), 1);
// need to append data to tspec
// make a copy of tspec->tspec_temp, increase its size, and
// copy data from tspec_temp -> tspec, then add new entries
Vector tspec_temp = tspec;
tspec.SetSize(ncomp*dof_cnt);
for (int i = 0; i < tspec_temp.Size(); i++)
{
tspec(i) = tspec_temp(i);
}
for (int i = 0; i < dof_cnt*vdim; i++)
{
tspec(i+(ncomp-vdim)*dof_cnt) = tspec_(i);
}
}
void DiscreteAdaptTC::SetTspecAtIndex(int idx, const GridFunction &tspec_)
{
const int vdim = tspec_.FESpace()->GetVDim(),
dof_cnt = tspec_.Size()/vdim;
for (int i = 0; i < dof_cnt*vdim; i++)
{
tspec(i+idx*dof_cnt) = tspec_(i);
}
FinalizeSerialDiscreteTargetSpec();
}
void DiscreteAdaptTC::SetSerialDiscreteTargetSize(const GridFunction &tspec_)
{
if (sizeidx > -1) { SetTspecAtIndex(sizeidx, tspec_); return; }
sizeidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
}
void DiscreteAdaptTC::SetSerialDiscreteTargetSkew(const GridFunction &tspec_)
{
if (skewidx > -1) { SetTspecAtIndex(skewidx, tspec_); return; }
skewidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
}
void DiscreteAdaptTC::SetSerialDiscreteTargetAspectRatio(
const GridFunction &tspec_)
{
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, tspec_); return; }
aspectratioidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
}
void DiscreteAdaptTC::SetSerialDiscreteTargetOrientation(
const GridFunction &tspec_)
{
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, tspec_); return; }
orientationidx = ncomp;
SetDiscreteTargetBase(tspec_);
FinalizeSerialDiscreteTargetSpec();
}
void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec()
{
MFEM_VERIFY(adapt_eval, "SetAdaptivityEvaluator() has not been called!")
MFEM_VERIFY(ncomp > 0, "No target specifications have been set!");
adapt_eval->SetSerialMetaInfo(*tspec_fes->GetMesh(),
*tspec_fes->FEColl(), ncomp);
adapt_eval->SetInitialField(*tspec_fes->GetMesh()->GetNodes(), tspec);
adapt_eval->SetSerialMetaInfo(*tspec_.FESpace()->GetMesh(),
*tspec_.FESpace()->FEColl(),
tspec_.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec_.FESpace()->GetMesh()->GetNodes(), tspec);
tspec_sav = tspec;
delete tspec_fesv;
tspec_fesv = new FiniteElementSpace(tspec_fes->GetMesh(),
tspec_fes->FEColl(), ncomp);
}
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(const GridFunction &tspec_)
{
SetSerialDiscreteTargetSize(tspec_);
FinalizeSerialDiscreteTargetSpec();
}
void DiscreteAdaptTC::UpdateTargetSpecification(const Vector &new_x,
bool use_flag)
{
@@ -1155,12 +1020,8 @@ void DiscreteAdaptTC::UpdateTargetSpecificationAtNode(const FiniteElement &el,
Array<int> dofs;
tspec_fes->GetElementDofs(T.ElementNo, dofs);
const int cnt = tspec.Size()/ncomp; //dofs per scalar-field
for (int i = 0; i < ncomp; i++)
{
tspec(dofs[dofidx]+i*cnt) = IntData(dofs[dofidx] + i*cnt + dir*cnt*ncomp);
}
int cnt = tspec.Size();
tspec(dofs[dofidx]) = IntData(dofs[dofidx]+dir*cnt);
}
void DiscreteAdaptTC::RestoreTargetSpecificationAtNode(ElementTransformation &T,
@@ -1170,11 +1031,7 @@ void DiscreteAdaptTC::RestoreTargetSpecificationAtNode(ElementTransformation &T,
Array<int> dofs;
tspec_fes->GetElementDofs(T.ElementNo, dofs);
const int cnt = tspec.Size()/ncomp;
for (int i = 0; i < ncomp; i++)
{
tspec(dofs[dofidx] + i*cnt) = tspec_sav(dofs[dofidx] + i*cnt);
}
tspec(dofs[dofidx]) = tspec_sav(dofs[dofidx]);
}
void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
@@ -1182,176 +1039,37 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_VERIFY(tspec_fesv, "No target specifications have been set.");
MFEM_VERIFY(tspec_fes, "A call to SetDiscreteTargerSpec() is needed.");
switch (target_type)
{
case IDEAL_SHAPE_GIVEN_SIZE:
case GIVEN_SHAPE_AND_SIZE:
{
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fe.GetGeomType());
const int dim = Wideal.Height(),
ndofs = tspec_fes->GetFE(0)->GetDof(),
ntspec_dofs = ndofs*ncomp;
Vector shape(ndofs), tspec_vals(ntspec_dofs), par_vals,
par_vals_c1(ndofs), par_vals_c2(ndofs), par_vals_c3(ndofs);
ntspec_dofs = tspec_fes->GetFE(0)->GetDof();
Vector shape(ntspec_dofs), tspec_vals(ntspec_dofs);
Array<int> dofs;
DenseMatrix D_rho(dim), Q_phi(dim), R_theta(dim);
tspec_fesv->GetElementVDofs(e_id, dofs);
tspec_fes->GetElementDofs(e_id, dofs);
tspec.GetSubVector(dofs, tspec_vals);
const double min_size = tspec_vals.Min();
MFEM_ASSERT(min_size > 0.0,
"Non-positive size propagated in the target definition.");
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
Jtr(i) = Wideal; //Initialize to identity
if (sizeidx != -1) //Set size
{
par_vals.SetDataAndSize(tspec_vals.GetData()+sizeidx*ndofs, ndofs);
const double min_size = par_vals.Min();
MFEM_VERIFY(min_size > 0.0,
"Non-positive size propagated in the target definition.");
const double size = std::max(shape * par_vals, min_size);
Jtr(i).Set(std::pow(size, 1.0/dim), Jtr(i));
} //Done size
if (target_type == IDEAL_SHAPE_GIVEN_SIZE) { continue; }
if (aspectratioidx != -1) //Set aspect ratio
{
if (dim == 2)
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
aspectratioidx*ndofs, ndofs);
const double aspectratio = shape * par_vals;
D_rho = 0.;
D_rho(0,0) = 1./pow(aspectratio,0.5);
D_rho(1,1) = pow(aspectratio,0.5);
}
else
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
aspectratioidx*ndofs, ndofs*3);
par_vals_c1.SetData(par_vals.GetData());
par_vals_c2.SetData(par_vals.GetData()+ndofs);
par_vals_c3.SetData(par_vals.GetData()+2*ndofs);
const double rho1 = shape * par_vals_c1;
const double rho2 = shape * par_vals_c2;
const double rho3 = shape * par_vals_c3;
D_rho = 0.;
D_rho(0,0) = pow(rho1,2./3.);
D_rho(1,1) = pow(rho2,2./3.);
D_rho(2,2) = pow(rho3,2./3.);
}
DenseMatrix Temp = Jtr(i);
Mult(D_rho, Temp, Jtr(i));
} //Done aspect ratio
if (skewidx != -1) //Set skew
{
if (dim == 2)
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
skewidx*ndofs, ndofs);
const double skew = shape * par_vals;
Q_phi = 0.;
Q_phi(0,0) = 1.;
Q_phi(0,1) = cos(skew);
Q_phi(1,1) = sin(skew);
}
else
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
skewidx*ndofs, ndofs*3);
par_vals_c1.SetData(par_vals.GetData());
par_vals_c2.SetData(par_vals.GetData()+ndofs);
par_vals_c3.SetData(par_vals.GetData()+2*ndofs);
const double phi12 = shape * par_vals_c1;
const double phi13 = shape * par_vals_c2;
const double chi = shape * par_vals_c3;
Q_phi = 0.;
Q_phi(0,0) = 1.;
Q_phi(0,1) = cos(phi12);
Q_phi(0,2) = cos(phi13);
Q_phi(1,1) = sin(phi12);
Q_phi(1,2) = sin(phi13)*cos(chi);
Q_phi(2,2) = sin(phi13)*sin(chi);
}
DenseMatrix Temp = Jtr(i);
Mult(Q_phi, Temp, Jtr(i));
} // done skew
if (orientationidx != -1) //Set orientation
{
if (dim == 2)
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
orientationidx*ndofs, ndofs);
const double theta = shape * par_vals;
R_theta(0,0) = cos(theta);
R_theta(0,1) = -sin(theta);
R_theta(1,0) = sin(theta);
R_theta(1,1) = cos(theta);
}
else
{
par_vals.SetDataAndSize(tspec_vals.GetData()+
orientationidx*ndofs, ndofs*3);
par_vals_c1.SetData(par_vals.GetData());
par_vals_c2.SetData(par_vals.GetData()+ndofs);
par_vals_c3.SetData(par_vals.GetData()+2*ndofs);
const double theta = shape * par_vals_c1;
const double psi = shape * par_vals_c2;
const double beta = shape * par_vals_c3;
DenseMatrix R_tp(dim), R_beta(dim), R_theta(dim);
double ct = cos(theta), st = sin(theta),
cp = cos(psi), sp = sin(psi);
R_tp(0,0) = ct*sp;
R_tp(1,0) = st*sp;
R_tp(2,0) = cp;
R_tp(0,1) = -(ct*st*sp*sp)/(1+cp);
R_tp(1,1) = cp+(pow(ct,2.)*pow(sp,2.))/(1+cp);
R_tp(2,1) = -st*sp;
R_tp(0,2) = -cp-(pow(st,2.)*pow(sp,2.))/(1+cp);
R_tp(1,2) = -R_tp(0,1);
R_tp(2,2) = ct*sp;
R_beta = 0.;
R_beta(0,0) = 1.;
R_beta(1,1) = cos(beta);
R_beta(1,2) = -sin(beta);
R_beta(2,1) = sin(beta);
R_beta(2,2) = cos(beta);
Mult(R_tp, R_beta, R_theta);
}
DenseMatrix Temp = Jtr(i);
Mult(R_theta, Temp, Jtr(i));
} // done orientation
const double size = std::max(shape * tspec_vals, min_size);
Jtr(i).Set(std::pow(size / Wideal.Det(), 1.0/dim), Wideal);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for discrete adaptation!");
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
@@ -1361,10 +1079,13 @@ void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
{
if (use_flag && good_tspec_grad) { return; }
const int dim = tspec_fes->GetFE(0)->GetDim(),
cnt = x.Size()/dim;
const int dim = tspec_fes->GetFE(0)->GetDim();
const int cnt = x.Size()/dim;
tspec_pert1h.SetSize(x.Size()*ncomp);
if (tspec_pert1h.Size() != x.Size())
{
tspec_pert1h.SetSize(x.Size());
}
Vector TSpecTemp;
Vector xtemp = x;
@@ -1372,7 +1093,7 @@ void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
{
for (int i = 0; i < cnt; i++) { xtemp(j*cnt+i) += dx; }
TSpecTemp.NewDataAndSize(tspec_pert1h.GetData() + j*cnt*ncomp, cnt*ncomp);
TSpecTemp.SetDataAndSize(tspec_pert1h.GetData() + j*cnt, cnt);
UpdateTargetSpecification(xtemp, TSpecTemp);
for (int i = 0; i < cnt; i++) { xtemp(j*cnt+i) -= dx; }
@@ -1384,15 +1105,16 @@ void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
double dx, bool use_flag)
{
if (use_flag && good_tspec_hess) { return; }
const int dim = tspec_fes->GetFE(0)->GetDim(),
cnt = x.Size()/dim,
totmix = 1+2*(dim-2);
const int dim = tspec_fes->GetFE(0)->GetDim();
const int cnt = x.Size()/dim;
tspec_pert2h.SetSize(cnt*dim*ncomp);
tspec_pertmix.SetSize(cnt*totmix*ncomp);
if (tspec_pert2h.Size() != x.Size())
{
tspec_pert2h.SetSize(x.Size());
tspec_pertmix.SetSize(cnt*(1+2*(dim-2)));
}
Vector TSpecTemp;
Vector xtemp = x;
@@ -1402,14 +1124,14 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
{
for (int i = 0; i < cnt; i++) { xtemp(j*cnt+i) += 2*dx; }
TSpecTemp.NewDataAndSize(tspec_pert2h.GetData() + j*cnt*ncomp, cnt*ncomp);
TSpecTemp.SetDataAndSize(tspec_pert2h.GetData() + j*cnt, cnt);
UpdateTargetSpecification(xtemp, TSpecTemp);
for (int i = 0; i < cnt; i++) { xtemp(j*cnt+i) -= 2*dx; }
}
// T(x+h,y+h)
int j = 0;
int idx = 0;
for (int k1 = 0; k1 < dim; k1++)
{
for (int k2 = 0; (k1 != k2) && (k2 < dim); k2++)
@@ -1420,7 +1142,7 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
xtemp(k2*cnt+i) += dx;
}
TSpecTemp.NewDataAndSize(tspec_pertmix.GetData() + j*cnt*ncomp, cnt*ncomp);
TSpecTemp.SetDataAndSize(tspec_pertmix.GetData() + idx*cnt, cnt);
UpdateTargetSpecification(xtemp, TSpecTemp);
for (int i = 0; i < cnt; i++)
@@ -1428,7 +1150,7 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
xtemp(k1*cnt+i) -= dx;
xtemp(k2*cnt+i) -= dx;
}
j++;
idx++;
}
}
@@ -1443,8 +1165,6 @@ void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
delete mesh;
mesh = new Mesh(m, true);
fes = new FiniteElementSpace(mesh, &fec, num_comp);
dim = fes->GetFE(0)->GetDim();
ncomp = num_comp;
}
#ifdef MFEM_USE_MPI
@@ -1456,8 +1176,6 @@ void AdaptivityEvaluator::SetParMetaInfo(const ParMesh &m,
delete pmesh;
pmesh = new ParMesh(m, true);
pfes = new ParFiniteElementSpace(pmesh, &fec, num_comp);
dim = pfes->GetFE(0)->GetDim();
ncomp = num_comp;
}
#endif
@@ -1549,7 +1267,6 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
Tpr->ElementNo = T.ElementNo;
Tpr->ElementType = ElementTransformation::ELEMENT;
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
}
@@ -1674,7 +1391,6 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
Tpr->ElementNo = T.ElementNo;
Tpr->ElementType = ElementTransformation::ELEMENT;
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
}
@@ -1768,7 +1484,6 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
Tpr->ElementNo = T.ElementNo;
Tpr->ElementType = ElementTransformation::ELEMENT;
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI);
}
+4 -47
View File
@@ -560,8 +560,6 @@ protected:
ParFiniteElementSpace *pfes;
#endif
int dim, ncomp;
public:
AdaptivityEvaluator() : mesh(NULL), fes(NULL)
{
@@ -710,20 +708,15 @@ class DiscreteAdaptTC : public TargetConstructor
protected:
// Discrete target specification.
// Data is owned, updated by UpdateTargetSpecification.
int ncomp, sizeidx, skewidx, aspectratioidx, orientationidx;
Vector tspec; //eta(x)
Vector tspec_sav;
Vector tspec_pert1h; //eta(x+h)
Vector tspec_pert2h; //eta(x+2*h)
Vector tspec_pertmix; //eta(x+h,y+h)
// The order inside these perturbation vectors (e.g. in 2D) is
// eta1(x+h,y), eta2(x+h,y) ... etan(x+h,y), eta1(x,y+h), eta2(x,y+h) ...
// same for tspec_pert2h and tspec_pertmix.
// Note: do not use the Nodes of this space as they may not be on the
// positions corresponding to the values of tspec.
const FiniteElementSpace *tspec_fes;
const FiniteElementSpace *tspec_fesv;
// These flags can be used by outside functions to avoid recomputing
// the tspec and tspec_perth fields again on the same mesh.
@@ -733,55 +726,20 @@ protected:
// Owned.
AdaptivityEvaluator *adapt_eval;
void SetDiscreteTargetBase(const GridFunction &tspec_);
void SetTspecAtIndex(int idx, const GridFunction &tspec_);
void FinalizeSerialDiscreteTargetSpec();
#ifdef MFEM_USE_MPI
void SetTspecAtIndex(int idx, const ParGridFunction &tspec_);
void FinalizeParDiscreteTargetSpec(const ParGridFunction &tspec_);
#endif
public:
DiscreteAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
ncomp(0),
sizeidx(-1), skewidx(-1), aspectratioidx(-1), orientationidx(-1),
tspec(), tspec_sav(), tspec_pert1h(), tspec_pert2h(), tspec_pertmix(),
tspec_fes(NULL), tspec_fesv(NULL),
tspec_fes(NULL),
good_tspec(false), good_tspec_grad(false), good_tspec_hess(false),
adapt_eval(NULL) { }
virtual ~DiscreteAdaptTC()
{
delete adapt_eval;
delete tspec_fes;
delete tspec_fesv;
}
virtual ~DiscreteAdaptTC() { delete adapt_eval; }
/** @name Target specification methods.
The following methods are used to specify geometric parameters of the
targets when these parameters are given by discrete FE functions.
Note that every GridFunction given to the Set methods must use a
H1_FECollection of the same order. The number of components must
correspond to the type of geometric parameter and dimension.
@param[in] tspec_ Input values of a geometric parameter. Note that
the methods in this class support only functions that
use H1_FECollection collection of the same order. */
///@{
virtual void SetSerialDiscreteTargetSpec(const GridFunction &tspec_);
virtual void SetSerialDiscreteTargetSize(const GridFunction &tspec_);
virtual void SetSerialDiscreteTargetSkew(const GridFunction &tspec_);
virtual void SetSerialDiscreteTargetAspectRatio(const GridFunction &tspec_);
virtual void SetSerialDiscreteTargetOrientation(const GridFunction &tspec_);
virtual void SetSerialDiscreteTargetSpec(GridFunction &tspec_);
#ifdef MFEM_USE_MPI
virtual void SetParDiscreteTargetSpec(const ParGridFunction &tspec_);
virtual void SetParDiscreteTargetSize(const ParGridFunction &tspec_);
virtual void SetParDiscreteTargetSkew(const ParGridFunction &tspec_);
virtual void SetParDiscreteTargetAspectRatio(const ParGridFunction &tspec_);
virtual void SetParDiscreteTargetOrientation(const ParGridFunction &tspec_);
virtual void SetParDiscreteTargetSpec(ParGridFunction &tspec_);
#endif
///@}
/// Used in combination with the Update methods to avoid extra computations.
void ResetUpdateFlags()
@@ -798,7 +756,6 @@ public:
ElementTransformation &T,
int nodenum, int idir,
const Vector &IntData);
void RestoreTargetSpecificationAtNode(ElementTransformation &T, int nodenum);
/** Used for finite-difference based computations. Computes the target
+26 -72
View File
@@ -29,26 +29,13 @@ void AdvectorCG::SetInitialField(const Vector &init_nodes,
void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field)
{
// TODO: Implement for AMR meshes.
const int pnt_cnt = new_field.Size()/ncomp;
new_field = field0;
for (int i = 0; i < ncomp; i++)
{
Vector new_field_temp(new_field.GetData()+i*pnt_cnt, pnt_cnt);
ComputeAtNewPositionScalar(new_nodes, new_field_temp);
}
field0 = new_field;
nodes0 = new_nodes;
}
void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
Vector &new_field)
{
#if defined(MFEM_DEBUG) || defined(MFEM_USE_MPI)
int myid = 0;
#endif
Mesh *m = mesh;
#ifdef MFEM_USE_MPI
if (pfes) { MPI_Comm_rank(pfes->GetComm(), &myid); }
if (pmesh) { m = pmesh; }
#endif
@@ -57,29 +44,17 @@ void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
// This will be used to move the positions.
GridFunction *mesh_nodes = m->GetNodes();
*mesh_nodes = nodes0;
double minv = new_field.Min(), maxv = new_field.Max();
new_field = field0;
// Velocity of the positions.
GridFunction u(mesh_nodes->FESpace());
subtract(new_nodes, nodes0, u);
// Define a scalar FE space for the solution, and the advection operator.
TimeDependentOperator *oper = NULL;
FiniteElementSpace *fess = NULL;
// This must be the fes of the ind, associated with the object's mesh.
if (fes) { oper = new SerialAdvectorCGOper(nodes0, u, *fes); }
#ifdef MFEM_USE_MPI
ParFiniteElementSpace *pfess = NULL;
#endif
if (fes)
{
fess = new FiniteElementSpace(fes->GetMesh(), fes->FEColl(), 1);
oper = new SerialAdvectorCGOper(nodes0, u, *fess);
}
#ifdef MFEM_USE_MPI
else if (pfes)
{
pfess = new ParFiniteElementSpace(pfes->GetParMesh(), pfes->FEColl(), 1);
oper = new ParAdvectorCGOper(nodes0, u, *pfess);
}
else if (pfes) { oper = new ParAdvectorCGOper(nodes0, u, *pfes); }
#endif
MFEM_VERIFY(oper != NULL,
"No FE space has been given to the AdaptivityEvaluator.");
@@ -92,18 +67,12 @@ void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
h_min = std::min(h_min, m->GetElementSize(i));
}
double v_max = 0.0;
const int s = new_field.Size();
const int s = u.FESpace()->GetVSize() / 2;
for (int i = 0; i < s; i++)
{
double vel = 0.;
for (int j = 0; j < dim; j++)
{
vel += u(i+j*s)*u(i+j*s);
}
const double vel = u(i) * u(i) + u(i+s) * u(i+s);
v_max = std::max(v_max, vel);
}
#ifdef MFEM_USE_MPI
if (pfes)
{
@@ -112,17 +81,12 @@ void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
MPI_Allreduce(&h_loc, &h_min, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
}
#endif
if (v_max == 0.0) // No need to change the field.
if (v_max == 0.0)
{
// No mesh motion --> no need to change the field.
delete oper;
delete fess;
#ifdef MFEM_USE_MPI
delete pfess;
#endif
return;
}
v_max = std::sqrt(v_max);
double dt = dt_scale * h_min / v_max;
@@ -132,34 +96,30 @@ void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
{
if (t + dt >= 1.0)
{
#ifdef MFEM_DEBUG
if (myid == 0)
{
mfem::out << "Remap took " << ti << " steps." << std::endl;
}
#endif
dt = 1.0 - t;
last_step = true;
}
ode_solver.Step(new_field, t, dt);
}
double glob_minv = minv,
glob_maxv = maxv;
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(&minv, &glob_minv, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
MPI_Allreduce(&maxv, &glob_maxv, 1, MPI_DOUBLE, MPI_MAX, pfes->GetComm());
}
#endif
// Trim the overshoots and undershoots.
for (int i = 0; i < s; i++)
const double minv = field0.Min(), maxv = field0.Max();
for (int i = 0; i < new_field.Size(); i++)
{
if (new_field(i) < glob_minv) { new_field(i) = glob_minv; }
if (new_field(i) > glob_maxv) { new_field(i) = glob_maxv; }
if (new_field(i) < minv) { new_field(i) = minv; }
if (new_field(i) > maxv) { new_field(i) = maxv; }
}
nodes0 = new_nodes;
field0 = new_field;
delete oper;
delete fess;
#ifdef MFEM_USE_MPI
delete pfess;
#endif
}
SerialAdvectorCGOper::SerialAdvectorCGOper(const Vector &x_start,
@@ -275,12 +235,6 @@ void InterpolatorFP::SetInitialField(const Vector &init_nodes,
const double newton_tol = 1.0e-12;
const int npts_at_once = 256;
if (finder)
{
finder->FreeData();
delete finder;
}
FiniteElementSpace *f = fes;
#ifdef MFEM_USE_MPI
if (pfes)
+1 -3
View File
@@ -27,9 +27,9 @@ private:
RK4Solver ode_solver;
Vector nodes0;
Vector field0;
const double dt_scale;
void ComputeAtNewPositionScalar(const Vector &new_nodes, Vector &new_field);
public:
AdvectorCG(double timestep_scale = 0.5)
: AdaptivityEvaluator(),
@@ -53,8 +53,6 @@ private:
Vector pos_r_out, dist_p_out;
int dim;
public:
InterpolatorFP() : finder(NULL) { }
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field);
+1 -3
View File
@@ -520,9 +520,7 @@ void TensorProductPRefinementTransferOperator::MultTranspose(const Vector& x,
TrueTransferOperator::TrueTransferOperator(const
ParFiniteElementSpace& lFESpace_,
const ParFiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetTrueVSize(), lFESpace_.GetTrueVSize()),
lFESpace(lFESpace_),
hFESpace(hFESpace_)
: lFESpace(lFESpace_), hFESpace(hFESpace_)
{
localTransferOperator = new TransferOperator(lFESpace_, hFESpace_);
-4
View File
@@ -15,8 +15,6 @@
#include "adios2stream.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../fem/geom.hpp"
#include "../general/array.hpp"
#include "../mesh/element.hpp"
@@ -752,5 +750,3 @@ noexcept
}
} // end namespace mfem
#endif // MFEM_USE_ADIOS2
-4
View File
@@ -18,8 +18,6 @@
#include "../config/config.hpp"
#ifdef MFEM_USE_ADIOS2
#include <map>
#include <memory> // std::unique_ptr
#include <string>
@@ -232,6 +230,4 @@ private:
} // end namespace mfem
#endif // MFEM_USE_ADIOS2
#endif /* MFEM_ADIOS2STREAM */
+7 -10
View File
@@ -26,10 +26,8 @@
#include <signal.h>
#include <sys/mman.h>
#define mfem_memalign(p,a,s) posix_memalign(p,a,s)
#define mfem_aligned_free free
#else
#define mfem_memalign(p,a,s) (((*(p))=_aligned_malloc((s),(a))),*(p)?0:errno)
#define mfem_aligned_free _aligned_free
#endif
#ifdef MFEM_USE_UMPIRE
@@ -214,7 +212,7 @@ public:
Aligned32HostMemorySpace(): HostMemorySpace() { }
void Alloc(void **ptr, size_t bytes)
{ if (mfem_memalign(ptr, 32, bytes) != 0) { throw ::std::bad_alloc(); } }
void Dealloc(void *ptr) { mfem_aligned_free(ptr); }
void Dealloc(void *ptr) { std::free(ptr); }
};
/// The aligned 64 host memory space
@@ -224,7 +222,6 @@ public:
Aligned64HostMemorySpace(): HostMemorySpace() { }
void Alloc(void **ptr, size_t bytes)
{ if (mfem_memalign(ptr, 64, bytes) != 0) { throw ::std::bad_alloc(); } }
void Dealloc(void *ptr) { mfem_aligned_free(ptr); }
};
#ifndef _WIN32
@@ -669,11 +666,12 @@ void *MemoryManager::Register_(void *ptr, void *h_tmp, size_t bytes,
{
MFEM_CONTRACT_VAR(alias);
MFEM_ASSERT(exists, "Internal error!");
MFEM_ASSERT(IsHostMemory(mt), "Internal error!");
MFEM_ASSERT(!alias, "Cannot register an alias!");
const bool is_host_mem = IsHostMemory(mt);
const MemType dual_mt = GetDualMemoryType_(mt);
const MemType h_mt = is_host_mem ? mt : dual_mt;
const MemType d_mt = is_host_mem ? dual_mt : mt;
const MemType h_mt = mt;
const MemType d_mt = dual_mt;
MFEM_VERIFY_TYPES(h_mt, d_mt);
if (ptr == nullptr && h_tmp == nullptr)
@@ -695,11 +693,10 @@ void *MemoryManager::Register_(void *ptr, void *h_tmp, size_t bytes,
else // DEVICE TYPES
{
h_ptr = h_tmp;
if (own && h_tmp == nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
if (h_tmp == nullptr) { ctrl->Host(h_mt)->Alloc(&h_ptr, bytes); }
mm.InsertDevice(ptr, h_ptr, bytes, h_mt, d_mt);
flags = own ? flags | Mem::OWNS_DEVICE : flags & ~Mem::OWNS_DEVICE;
flags = own ? flags | Mem::OWNS_HOST : flags & ~Mem::OWNS_HOST;
flags |= Mem::VALID_DEVICE;
flags = (own ? flags | Mem::OWNS_DEVICE : flags & ~Mem::OWNS_DEVICE) |
Mem::OWNS_HOST | Mem::VALID_DEVICE;
}
CheckHostMemoryType_(h_mt, h_ptr);
return h_ptr;
+3 -72
View File
@@ -16,7 +16,6 @@
#include "error.hpp"
#include <cstring> // std::memcpy
#include <type_traits> // std::is_const
#include <cstddef> // std::max_align_t
namespace mfem
{
@@ -284,28 +283,6 @@ public:
@note The current memory is NOT deleted by this method. */
inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
/** Wrap an externally pair of allocated pointers, @a h_ptr and @ d_ptr,
of the given host MemoryType @a h_mt. */
/** The new memory object will have the device MemoryType set as valid.
The given @a h_ptr and @a d_ptr must be allocated appropriately for the
given host MemoryType and its associated device MemoryType:
- MANAGED => MANAGED,
- HOST_DEBUG => DEVICE_DEBUG,
- HOST_UMPIRE => DEVICE_UMPIRE,
- HOST, HOST_32, HOST_64 => DEVICE.
The parameter @a own determines whether both @a h_ptr and @a d_ptr will
be deleted when the method Delete() is called.
@note Ownership can also be controled by using the folowing methods:
- ClearOwnerFlags,
- SetHostPtrOwner,
- SetDevicePtrOwner.
@note The current memory is NOT deleted by this method. */
inline void Wrap(T *h_ptr, T *d_ptr, int size, MemoryType h_mt, bool own);
/// Create a memory object that points inside the memory object @a base.
/** The new Memory object uses the same MemoryType(s) as @a base.
@@ -436,38 +413,6 @@ public:
/** This method can be useful for debugging. It is explicitly instantiated
for Memory<T> with T = int and T = double. */
inline int CompareHostAndDevice(int size) const;
private:
// GCC 4.8 workaround: max_align_t is not in std.
static constexpr std::size_t def_align_bytes_()
{
using namespace std;
return alignof(max_align_t);
}
static constexpr std::size_t def_align_bytes = def_align_bytes_();
static constexpr std::size_t new_align_bytes =
alignof(T) > def_align_bytes ? alignof(T) : def_align_bytes;
template <std::size_t align_bytes, bool dummy = true> struct Alloc
{
static inline T *New(std::size_t)
{
#if __cplusplus < 201703L
// Generate an error in debug mode
MFEM_ASSERT(false, "overaligned type cannot use MemoryType::HOST");
return nullptr;
#else
return new T[size];
#endif
}
};
#if __cplusplus < 201703L
template<bool dummy> struct Alloc<def_align_bytes,dummy>
{
static inline T *New(std::size_t size) { return new T[size]; }
};
#endif
};
@@ -680,7 +625,7 @@ inline void Memory<T>::New(int size)
capacity = size;
flags = OWNS_HOST | VALID_HOST;
h_mt = MemoryManager::host_mem_type;
h_ptr = (h_mt == MemoryType::HOST) ? Alloc<new_align_bytes>::New(size) :
h_ptr = (h_mt == MemoryType::HOST) ? new T[size] :
(T*)MemoryManager::New_(nullptr, size*sizeof(T), h_mt, flags);
}
@@ -692,9 +637,8 @@ inline void Memory<T>::New(int size, MemoryType mt)
const bool mt_host = mt == MemoryType::HOST;
if (mt_host) { flags = OWNS_HOST | VALID_HOST; }
h_mt = IsHostMemory(mt) ? mt : MemoryManager::GetDualMemoryType_(mt);
T *h_tmp = (h_mt == MemoryType::HOST) ?
Alloc<new_align_bytes>::New(size) : nullptr;
h_ptr = (mt_host) ? h_tmp : (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
T *h_tmp = (h_mt == MemoryType::HOST) ? new T[size] : nullptr;
h_ptr = (mt_host) ? h_tmp: (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
}
template <typename T>
@@ -738,19 +682,6 @@ inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
own, false, flags);
}
template <typename T>
inline void Memory<T>::Wrap(T *ptr, T *d_ptr, int size, MemoryType mt, bool own)
{
h_mt = mt;
flags = 0;
h_ptr = ptr;
capacity = size;
MFEM_ASSERT(IsHostMemory(h_mt),"");
const size_t bytes = size*sizeof(T);
const MemoryType d_mt = MemoryManager::GetDualMemoryType_(h_mt);
MemoryManager::Register_(d_ptr, h_ptr, bytes, d_mt, own, false, flags);
}
template <typename T>
inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
{
+6 -46
View File
@@ -42,15 +42,9 @@ struct AssignOp_Impl;
template <>
struct AssignOp_Impl<AssignOp::Set>
{
template <typename lvalue_t, typename rvalue_t>
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
return (a = b);
}
template <typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
static inline lvalue_t &AssignHD(lvalue_t &a, const rvalue_t &b)
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
return (a = b);
}
@@ -59,16 +53,9 @@ struct AssignOp_Impl<AssignOp::Set>
template <>
struct AssignOp_Impl<AssignOp::Add>
{
template <typename lvalue_t, typename rvalue_t>
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a += b);
}
template <typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
static inline lvalue_t &AssignHD(lvalue_t &a, const rvalue_t &b)
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a += b);
@@ -78,16 +65,9 @@ struct AssignOp_Impl<AssignOp::Add>
template <>
struct AssignOp_Impl<AssignOp::Mult>
{
template <typename lvalue_t, typename rvalue_t>
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a *= b);
}
template <typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
static inline lvalue_t &AssignHD(lvalue_t &a, const rvalue_t &b)
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a *= b);
@@ -97,16 +77,9 @@ struct AssignOp_Impl<AssignOp::Mult>
template <>
struct AssignOp_Impl<AssignOp::Div>
{
template <typename lvalue_t, typename rvalue_t>
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a /= b);
}
template <typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
static inline lvalue_t &AssignHD(lvalue_t &a, const rvalue_t &b)
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a /= b);
@@ -116,16 +89,9 @@ struct AssignOp_Impl<AssignOp::Div>
template <>
struct AssignOp_Impl<AssignOp::rDiv>
{
template <typename lvalue_t, typename rvalue_t>
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a = b/a);
}
template <typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
static inline lvalue_t &AssignHD(lvalue_t &a, const rvalue_t &b)
static inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
MFEM_FLOPS_ADD(1);
return (a = b/a);
@@ -135,18 +101,12 @@ struct AssignOp_Impl<AssignOp::rDiv>
} // namespace mfem::internal
template <AssignOp::Type Op, typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
inline lvalue_t &Assign(lvalue_t &a, const rvalue_t &b)
{
return internal::AssignOp_Impl<Op>::Assign(a, b);
}
template <AssignOp::Type Op, typename lvalue_t, typename rvalue_t>
MFEM_HOST_DEVICE
inline lvalue_t &AssignHD(lvalue_t &a, const rvalue_t &b)
{
return internal::AssignOp_Impl<Op>::AssignHD(a, b);
}
} // namespace mfem
#endif // MFEM_TEMPLATE_ASSIGN
-3
View File
@@ -148,9 +148,6 @@ const char *GetConfigStr()
#ifdef MFEM_USE_OCCA
"MFEM_USE_OCCA\n"
#endif
#ifdef MFEM_USE_SIMD
"MFEM_USE_SIMD\n"
#endif
#ifdef MFEM_USE_ADIOS2
"MFEM_USE_ADIOS2\n"
#endif
+1 -1
View File
@@ -175,7 +175,7 @@ struct static_method_holder
is_p->peek();
peek_failed = is_p->fail();
}
catch (std::ios_base::failure&) {}
catch (std::ios_base::failure &e) {}
if (peek_failed)
{
throw Exception(std::string("strict_fstream: open('")
+4 -1
View File
@@ -3043,6 +3043,9 @@ void LUFactors::RightSolve(int m, int n, double *X) const
}
#else
// compiling without LAPACK
const double *data = this->data;
const int *ipiv = this->ipiv;
// X <- X U^{-1}
x = X;
for (int k = 0; k < n; k++)
@@ -3077,7 +3080,7 @@ void LUFactors::RightSolve(int m, int n, double *X) const
x = X;
for (int k = 0; k < n; k++)
{
for (int i = m-1; i >= 0; --i)
for (int i = 0; i < m; i++)
{
Swap<double>(x[i*n], x[(ipiv[i]-ipiv_base)*n]);
}
+55 -313
View File
@@ -21,6 +21,55 @@
#include <cmath>
#include <cstdlib>
// Define macro wrappers for hypre_TAlloc, hypre_CTAlloc and hypre_TFree:
// mfem_hypre_TAlloc, mfem_hypre_CTAlloc, and mfem_hypre_TFree, respectively.
// Note: the same macros are defined in hypre_parcsr.cpp.
#if MFEM_HYPRE_VERSION < 21400
#define mfem_hypre_TAlloc(type, size) hypre_TAlloc(type, size)
#define mfem_hypre_CTAlloc(type, size) hypre_CTAlloc(type, size)
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr)
#else // MFEM_HYPRE_VERSION >= 21400
#define mfem_hypre_TAlloc(type, size) \
hypre_TAlloc(type, size, HYPRE_MEMORY_HOST)
#define mfem_hypre_CTAlloc(type, size) \
hypre_CTAlloc(type, size, HYPRE_MEMORY_HOST)
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr, HYPRE_MEMORY_HOST)
// Notes regarding allocation and deallocation of hypre objects in 2.14.0
//-----------------------------------------------------------------------
//
// 1. hypre_CSRMatrix: i, j, data, and rownnz use HYPRE_MEMORY_SHARED while the
// hypre_CSRMatrix structure uses HYPRE_MEMORY_HOST.
//
// Note: the function HYPRE_CSRMatrixCreate creates the i array using
// HYPRE_MEMORY_HOST!
// Note: the functions hypre_CSRMatrixAdd and hypre_CSRMatrixMultiply create
// C_i using HYPRE_MEMORY_HOST!
//
// 2. hypre_Vector: data uses HYPRE_MEMORY_SHARED while the hypre_Vector
// structure uses HYPRE_MEMORY_HOST.
//
// 3. hypre_ParVector: the structure hypre_ParVector uses HYPRE_MEMORY_HOST;
// partitioning uses HYPRE_MEMORY_HOST.
//
// 4. hypre_ParCSRMatrix: the structure hypre_ParCSRMatrix uses
// HYPRE_MEMORY_HOST; col_map_offd, row_starts, col_starts, rowindices,
// rowvalues also use HYPRE_MEMORY_HOST.
//
// Note: the function hypre_ParCSRMatrixToCSRMatrixAll allocates matrix_i
// using HYPRE_MEMORY_HOST!
//
// 5. The goal for the MFEM wrappers of hypre objects is to support only the
// standard hypre build case, i.e. when hypre is build without device support
// and all memory types correspond to host memory. In this case memory
// allocated with operator new can be used by hypre but (as usual) it must
// not be owned by hypre.
#endif // #if MFEM_HYPRE_VERSION < 21400
using namespace std;
namespace mfem
@@ -1664,293 +1713,6 @@ HypreParMatrix * RAP(const HypreParMatrix * Rt, const HypreParMatrix *A,
return new HypreParMatrix(rap);
}
// Helper function for HypreParMatrixFromBlocks. Note that scalability to
// extremely large processor counts is limited by the use of MPI_Allgather.
void GatherBlockOffsetData(MPI_Comm comm, const int rank, const int nprocs,
const int num_loc, Array<int> &offsets,
std::vector<int> &all_num_loc, const int numBlocks,
std::vector<std::vector<int>> &blockProcOffsets,
std::vector<int> &procOffsets,
std::vector<std::vector<int>> &procBlockOffsets,
int &firstLocal, int &globalNum)
{
std::vector<std::vector<int>> all_block_num_loc(numBlocks);
MPI_Allgather(&num_loc, 1, MPI_INT, all_num_loc.data(), 1, MPI_INT, comm);
for (int j = 0; j < numBlocks; ++j)
{
all_block_num_loc[j].resize(nprocs);
blockProcOffsets[j].resize(nprocs);
const int blockNumRows = offsets[j + 1] - offsets[j];
MPI_Allgather(&blockNumRows, 1, MPI_INT, all_block_num_loc[j].data(), 1,
MPI_INT, comm);
blockProcOffsets[j][0] = 0;
for (int i = 0; i < nprocs - 1; ++i)
{
blockProcOffsets[j][i + 1] = blockProcOffsets[j][i]
+ all_block_num_loc[j][i];
}
}
firstLocal = 0;
globalNum = 0;
procOffsets[0] = 0;
for (int i = 0; i < nprocs; ++i)
{
globalNum += all_num_loc[i];
if (i < rank)
{
firstLocal += all_num_loc[i];
}
if (i < nprocs - 1)
{
procOffsets[i + 1] = procOffsets[i] + all_num_loc[i];
}
procBlockOffsets[i].resize(numBlocks);
procBlockOffsets[i][0] = 0;
for (int j = 1; j < numBlocks; ++j)
{
procBlockOffsets[i][j] = procBlockOffsets[i][j - 1]
+ all_block_num_loc[j - 1][i];
}
}
}
HypreParMatrix * HypreParMatrixFromBlocks(Array2D<HypreParMatrix*> &blocks,
Array2D<double> *blockCoeff)
{
const int numBlockRows = blocks.NumRows();
const int numBlockCols = blocks.NumCols();
MFEM_VERIFY(numBlockRows > 0 &&
numBlockCols > 0, "Invalid input to HypreParMatrixFromBlocks");
if (blockCoeff != NULL)
{
MFEM_VERIFY(numBlockRows == blockCoeff->NumRows() &&
numBlockCols == blockCoeff->NumCols(),
"Invalid input to HypreParMatrixFromBlocks");
}
Array<int> rowOffsets(numBlockRows+1);
Array<int> colOffsets(numBlockCols+1);
int nonNullBlockRow0 = -1;
for (int j=0; j<numBlockCols; ++j)
{
if (blocks(0,j) != NULL)
{
nonNullBlockRow0 = j;
break;
}
}
MFEM_VERIFY(nonNullBlockRow0 >= 0, "Null row of blocks");
MPI_Comm comm = blocks(0,nonNullBlockRow0)->GetComm();
// Set offsets based on the number of rows or columns in each block.
rowOffsets = 0;
colOffsets = 0;
for (int i=0; i<numBlockRows; ++i)
{
for (int j=0; j<numBlockCols; ++j)
{
if (blocks(i,j) != NULL)
{
const int nrows = blocks(i,j)->NumRows();
const int ncols = blocks(i,j)->NumCols();
MFEM_VERIFY(nrows > 0 &&
ncols > 0, "Invalid block in HypreParMatrixFromBlocks");
if (rowOffsets[i+1] == 0)
{
rowOffsets[i+1] = nrows;
}
else
{
MFEM_VERIFY(rowOffsets[i+1] == nrows,
"Inconsistent blocks in HypreParMatrixFromBlocks");
}
if (colOffsets[j+1] == 0)
{
colOffsets[j+1] = ncols;
}
else
{
MFEM_VERIFY(colOffsets[j+1] == ncols,
"Inconsistent blocks in HypreParMatrixFromBlocks");
}
}
}
MFEM_VERIFY(rowOffsets[i+1] > 0, "Invalid input blocks");
rowOffsets[i+1] += rowOffsets[i];
}
for (int j=0; j<numBlockCols; ++j)
{
MFEM_VERIFY(colOffsets[j+1] > 0, "Invalid input blocks");
colOffsets[j+1] += colOffsets[j];
}
const int num_loc_rows = rowOffsets[numBlockRows];
const int num_loc_cols = colOffsets[numBlockCols];
int nprocs, rank;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nprocs);
std::vector<int> all_num_loc_rows(nprocs);
std::vector<int> all_num_loc_cols(nprocs);
std::vector<int> procRowOffsets(nprocs);
std::vector<int> procColOffsets(nprocs);
std::vector<std::vector<int>> blockRowProcOffsets(numBlockRows);
std::vector<std::vector<int>> blockColProcOffsets(numBlockCols);
std::vector<std::vector<int>> procBlockRowOffsets(nprocs);
std::vector<std::vector<int>> procBlockColOffsets(nprocs);
int first_loc_row, glob_nrows, first_loc_col, glob_ncols;
GatherBlockOffsetData(comm, rank, nprocs, num_loc_rows, rowOffsets,
all_num_loc_rows, numBlockRows, blockRowProcOffsets,
procRowOffsets, procBlockRowOffsets, first_loc_row,
glob_nrows);
GatherBlockOffsetData(comm, rank, nprocs, num_loc_cols, colOffsets,
all_num_loc_cols, numBlockCols, blockColProcOffsets,
procColOffsets, procBlockColOffsets, first_loc_col,
glob_ncols);
std::vector<int> opI(num_loc_rows + 1);
std::vector<int> cnt(num_loc_rows);
for (int i = 0; i < num_loc_rows; ++i)
{
opI[i] = 0;
cnt[i] = 0;
}
opI[num_loc_rows] = 0;
Array2D<hypre_CSRMatrix *> csr_blocks(numBlockRows, numBlockCols);
// Loop over all blocks, to determine nnz for each row.
for (int i = 0; i < numBlockRows; ++i)
{
for (int j = 0; j < numBlockCols; ++j)
{
if (blocks(i, j) == NULL)
{
csr_blocks(i, j) = NULL;
}
else
{
{
hypre_ParCSRMatrix *parcsr_op = (hypre_ParCSRMatrix*)
const_cast<HypreParMatrix&>
(*(blocks(i, j)));
MFEM_ASSERT(parcsr_op != NULL, "const_cast failed");
csr_blocks(i, j) = hypre_MergeDiagAndOffd(parcsr_op);
}
for (int k = 0; k < csr_blocks(i, j)->num_rows; ++k)
{
opI[rowOffsets[i] + k + 1] +=
csr_blocks(i, j)->i[k + 1] - csr_blocks(i, j)->i[k];
}
}
}
}
// Now opI[i] is nnz for row i-1. Do a partial sum to get offsets.
for (int i = 0; i < num_loc_rows; ++i)
{
opI[i + 1] += opI[i];
}
const int nnz = opI[num_loc_rows];
std::vector<HYPRE_Int> opJ(nnz);
std::vector<double> data(nnz);
// Loop over all blocks, to set matrix data.
for (int i = 0; i < numBlockRows; ++i)
{
for (int j = 0; j < numBlockCols; ++j)
{
if (csr_blocks(i, j) != NULL)
{
const int nrows = csr_blocks(i, j)->num_rows;
const double cij = blockCoeff ? (*blockCoeff)(i, j) : 1.0;
for (int k = 0; k < nrows; ++k)
{
const int rowg = rowOffsets[i] + k; // process-local row
const int nnz_k = csr_blocks(i,j)->i[k+1]-csr_blocks(i,j)->i[k];
const int osk = csr_blocks(i, j)->i[k];
for (int l = 0; l < nnz_k; ++l)
{
// Find the column process offset for the block.
const int bcol = csr_blocks(i, j)->j[osk + l];
int bcolproc = 0;
for (int p = 1; p < nprocs; ++p)
{
if (blockColProcOffsets[j][p] > bcol)
{
bcolproc = p - 1;
break;
}
}
if (blockColProcOffsets[j][nprocs - 1] <= bcol)
{
bcolproc = nprocs - 1;
}
opJ[opI[rowg] + cnt[rowg]] = procColOffsets[bcolproc] +
procBlockColOffsets[bcolproc][j]
+ bcol
- blockColProcOffsets[j][bcolproc];
data[opI[rowg] + cnt[rowg]] = cij * csr_blocks(i, j)->data[osk + l];
cnt[rowg]++;
}
}
}
}
}
for (int i = 0; i < numBlockRows; ++i)
{
for (int j = 0; j < numBlockCols; ++j)
{
if (csr_blocks(i, j) != NULL)
{
hypre_CSRMatrixDestroy(csr_blocks(i, j));
}
}
}
std::vector<HYPRE_Int> rowStarts2(2);
rowStarts2[0] = first_loc_row;
rowStarts2[1] = first_loc_row + all_num_loc_rows[rank];
std::vector<HYPRE_Int> colStarts2(2);
colStarts2[0] = first_loc_col;
colStarts2[1] = first_loc_col + all_num_loc_cols[rank];
return new HypreParMatrix(comm, num_loc_rows, glob_nrows, glob_ncols,
(int *)opI.data(), (HYPRE_Int *)opJ.data(),
(double *)data.data(),
(HYPRE_Int *)rowStarts2.data(),
(HYPRE_Int *)colStarts2.data());
}
void EliminateBC(HypreParMatrix &A, HypreParMatrix &Ae,
const Array<int> &ess_dof_list,
const Vector &X, Vector &B)
@@ -2130,7 +1892,6 @@ HypreSmoother::HypreSmoother() : Solver()
l1_norms = NULL;
pos_l1_norms = false;
eig_est_cg_iter = 10;
B = X = V = Z = NULL;
X0 = X1 = NULL;
fir_coeffs = NULL;
@@ -2138,7 +1899,7 @@ HypreSmoother::HypreSmoother() : Solver()
HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
int _relax_times, double _relax_weight, double _omega,
int _poly_order, double _poly_fraction, int _eig_est_cg_iter)
int _poly_order, double _poly_fraction)
{
type = _type;
relax_times = _relax_times;
@@ -2146,7 +1907,6 @@ HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
omega = _omega;
poly_order = _poly_order;
poly_fraction = _poly_fraction;
eig_est_cg_iter = _eig_est_cg_iter;
l1_norms = NULL;
pos_l1_norms = false;
@@ -2169,12 +1929,10 @@ void HypreSmoother::SetSOROptions(double _relax_weight, double _omega)
omega = _omega;
}
void HypreSmoother::SetPolyOptions(int _poly_order, double _poly_fraction,
int _eig_est_cg_iter)
void HypreSmoother::SetPolyOptions(int _poly_order, double _poly_fraction)
{
poly_order = _poly_order;
poly_fraction = _poly_fraction;
eig_est_cg_iter = _eig_est_cg_iter;
}
void HypreSmoother::SetTaubinOptions(double _lambda, double _mu,
@@ -2258,31 +2016,15 @@ void HypreSmoother::SetOperator(const Operator &op)
if (type == 16)
{
poly_scale = 1;
if (eig_est_cg_iter > 0)
{
hypre_ParCSRMaxEigEstimateCG(*A, poly_scale, eig_est_cg_iter,
&max_eig_est, &min_eig_est);
}
else
{
min_eig_est = 0;
hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est);
}
hypre_ParCSRMaxEigEstimateCG(*A, poly_scale, 10,
&max_eig_est, &min_eig_est);
Z = new HypreParVector(*A);
}
else if (type == 1001 || type == 1002)
{
poly_scale = 0;
if (eig_est_cg_iter > 0)
{
hypre_ParCSRMaxEigEstimateCG(*A, poly_scale, eig_est_cg_iter,
&max_eig_est, &min_eig_est);
}
else
{
min_eig_est = 0;
hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est);
}
hypre_ParCSRMaxEigEstimateCG(*A, poly_scale, 10,
&max_eig_est, &min_eig_est);
// The Taubin and FIR polynomials are defined on [0, 2]
max_eig_est /= 2;
+2 -18
View File
@@ -570,17 +570,6 @@ HypreParMatrix * RAP(const HypreParMatrix *A, const HypreParMatrix *P);
HypreParMatrix * RAP(const HypreParMatrix * Rt, const HypreParMatrix *A,
const HypreParMatrix *P);
/// Returns a merged hypre matrix constructed from hypre matrix blocks.
/** It is assumed that all block matrices use the same communicator, and the
block sizes are consistent in rows and columns. Rows and columns are
renumbered but not redistributed in parallel, e.g. the block rows owned by
each process remain on that process in the resulting matrix. Some blocks can
be NULL. Each block and the entire system can be rectangular. Scalability to
extremely large processor counts is limited by global MPI communication, see
GatherBlockOffsetData in hypre.cpp. */
HypreParMatrix * HypreParMatrixFromBlocks(Array2D<HypreParMatrix*> &blocks,
Array2D<double> *blockCoeff=NULL);
/** Eliminate essential BC specified by 'ess_dof_list' from the solution X to
the r.h.s. B. Here A is a matrix with eliminated BC, while Ae is such that
(A+Ae) is the original (Neumann) matrix before elimination. */
@@ -626,8 +615,6 @@ protected:
double *l1_norms;
/// If set, take absolute values of the computed l1_norms
bool pos_l1_norms;
/// Number of CG iterations to determine eigenvalue estimates
int eig_est_cg_iter;
/// Maximal eigenvalue estimate for polynomial smoothing
double max_eig_est;
/// Minimal eigenvalue estimate for polynomial smoothing
@@ -658,17 +645,14 @@ public:
HypreSmoother(HypreParMatrix &_A, int type = l1GS,
int relax_times = 1, double relax_weight = 1.0,
double omega = 1.0, int poly_order = 2,
double poly_fraction = .3, int eig_est_cg_iter = 10);
double poly_fraction = .3);
/// Set the relaxation type and number of sweeps
void SetType(HypreSmoother::Type type, int relax_times = 1);
/// Set SOR-related parameters
void SetSOROptions(double relax_weight, double omega);
/// Set parameters for polynomial smoothing
/** By default, 10 iterations of CG are used to estimate the eigenvalues.
Setting eig_est_cg_iter = 0 uses hypre's hypre_ParCSRMaxEigEstimate() instead. */
void SetPolyOptions(int poly_order, double poly_fraction,
int eig_est_cg_iter = 10);
void SetPolyOptions(int poly_order, double poly_fraction);
/// Set parameters for Taubin's lambda-mu method
void SetTaubinOptions(double lambda, double mu, int iter);
+20
View File
@@ -17,6 +17,26 @@
#include "hypre_parcsr.hpp"
#include <limits>
// Define macro wrappers for hypre_TAlloc, hypre_CTAlloc and hypre_TFree:
// mfem_hypre_TAlloc, mfem_hypre_CTAlloc, and mfem_hypre_TFree, respectively.
// Note: the same macros are defined in hypre.cpp.
#if MFEM_HYPRE_VERSION < 21400
#define mfem_hypre_TAlloc(type, size) hypre_TAlloc(type, size)
#define mfem_hypre_CTAlloc(type, size) hypre_CTAlloc(type, size)
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr)
#else // MFEM_HYPRE_VERSION >= 21400
// See the notes about hypre 2.14.0 in hypre.cpp
#define mfem_hypre_TAlloc(type, size) \
hypre_TAlloc(type, size, HYPRE_MEMORY_HOST)
#define mfem_hypre_CTAlloc(type, size) \
hypre_CTAlloc(type, size, HYPRE_MEMORY_HOST)
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr, HYPRE_MEMORY_HOST)
#endif // #if MFEM_HYPRE_VERSION < 21400
namespace mfem
{
namespace internal
-50
View File
@@ -21,56 +21,6 @@
#include "_hypre_parcsr_mv.h"
// Define macro wrappers for hypre_TAlloc, hypre_CTAlloc and hypre_TFree:
// mfem_hypre_TAlloc, mfem_hypre_CTAlloc, and mfem_hypre_TFree, respectively.
// Note: these macros are used in hypre.cpp, hypre_parcsr.cpp, and perhaps
// other locations in the future.
#if MFEM_HYPRE_VERSION < 21400
#define mfem_hypre_TAlloc(type, size) hypre_TAlloc(type, size)
#define mfem_hypre_CTAlloc(type, size) hypre_CTAlloc(type, size)
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr)
#else // MFEM_HYPRE_VERSION >= 21400
#define mfem_hypre_TAlloc(type, size) \
hypre_TAlloc(type, size, HYPRE_MEMORY_HOST)
#define mfem_hypre_CTAlloc(type, size) \
hypre_CTAlloc(type, size, HYPRE_MEMORY_HOST)
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr, HYPRE_MEMORY_HOST)
// Notes regarding allocation and deallocation of hypre objects in 2.14.0
//-----------------------------------------------------------------------
//
// 1. hypre_CSRMatrix: i, j, data, and rownnz use HYPRE_MEMORY_SHARED while the
// hypre_CSRMatrix structure uses HYPRE_MEMORY_HOST.
//
// Note: the function HYPRE_CSRMatrixCreate creates the i array using
// HYPRE_MEMORY_HOST!
// Note: the functions hypre_CSRMatrixAdd and hypre_CSRMatrixMultiply create
// C_i using HYPRE_MEMORY_HOST!
//
// 2. hypre_Vector: data uses HYPRE_MEMORY_SHARED while the hypre_Vector
// structure uses HYPRE_MEMORY_HOST.
//
// 3. hypre_ParVector: the structure hypre_ParVector uses HYPRE_MEMORY_HOST;
// partitioning uses HYPRE_MEMORY_HOST.
//
// 4. hypre_ParCSRMatrix: the structure hypre_ParCSRMatrix uses
// HYPRE_MEMORY_HOST; col_map_offd, row_starts, col_starts, rowindices,
// rowvalues also use HYPRE_MEMORY_HOST.
//
// Note: the function hypre_ParCSRMatrixToCSRMatrixAll allocates matrix_i
// using HYPRE_MEMORY_HOST!
//
// 5. The goal for the MFEM wrappers of hypre objects is to support only the
// standard hypre build case, i.e. when hypre is build without device support
// and all memory types correspond to host memory. In this case memory
// allocated with operator new can be used by hypre but (as usual) it must
// not be owned by hypre.
#endif // #if MFEM_HYPRE_VERSION < 21400
namespace mfem
{

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