Compare commits
331
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bef1cc28f | ||
|
|
6b45d9a69b | ||
|
|
db32ae9807 | ||
|
|
5ce6e90ceb | ||
|
|
03ba184adb | ||
|
|
cfa87477da | ||
|
|
939932bc68 | ||
|
|
d3ae34710c | ||
|
|
9b652996b2 | ||
|
|
5cfbbe5fad | ||
|
|
71909cd5e3 | ||
|
|
a5a3169064 | ||
|
|
afded067a7 | ||
|
|
629e93afd9 | ||
|
|
3e277808a9 | ||
|
|
864fb1ce9e | ||
|
|
ac7415cc69 | ||
|
|
38030d4395 | ||
|
|
2c96dc6a1f | ||
|
|
ecbc7bf8c2 | ||
|
|
5b5a21edac | ||
|
|
1f84ba036e | ||
|
|
76d0312309 | ||
|
|
c7ed339260 | ||
|
|
f1b3a33fb2 | ||
|
|
e3dedbbd5b | ||
|
|
261f3805b8 | ||
|
|
b386b2d6b6 | ||
|
|
6e98055eb7 | ||
|
|
5abd44f212 | ||
|
|
75012728db | ||
|
|
1f094244f8 | ||
|
|
cc16ddadbf | ||
|
|
8acd5cd3a2 | ||
|
|
1f5bc1c3d8 | ||
|
|
3588d47ec1 | ||
|
|
e7f5996bdf | ||
|
|
560ad1b5a3 | ||
|
|
f0fe5b0ec0 | ||
|
|
a92983051a | ||
|
|
37843b050c | ||
|
|
436714f5ef | ||
|
|
1ceef4f786 | ||
|
|
6bb6745c0e | ||
|
|
60f47c287d | ||
|
|
545a257ab5 | ||
|
|
164ee942c8 | ||
|
|
16c4fbdd29 | ||
|
|
e28093274b | ||
|
|
87dd19e6c0 | ||
|
|
678f53c306 | ||
|
|
4b4aff736d | ||
|
|
727339e0ae | ||
|
|
d8d4d5b874 | ||
|
|
5a7a017a4e | ||
|
|
224aff5ac9 | ||
|
|
e42762365f | ||
|
|
765fb4a608 | ||
|
|
1861b80627 | ||
|
|
a8c0c856f6 | ||
|
|
32fb4bf244 | ||
|
|
dbb86c87c2 | ||
|
|
e866ede0c4 | ||
|
|
2d021685de | ||
|
|
c498d9c759 | ||
|
|
2896b2fb02 | ||
|
|
8efb9483bc | ||
|
|
5507772f37 | ||
|
|
591156be2f | ||
|
|
d6ea262498 | ||
|
|
1b20704b24 | ||
|
|
55bafba69a | ||
|
|
3a29fda4cd | ||
|
|
6b147fd9ff | ||
|
|
c53bce08f1 | ||
|
|
afadd435f5 | ||
|
|
36d7629b26 | ||
|
|
8f32e6620b | ||
|
|
766760659d | ||
|
|
2c0419a9a8 | ||
|
|
9185762478 | ||
|
|
2c41e483fe | ||
|
|
78a4a77ed7 | ||
|
|
ea4aceeffc | ||
|
|
1f8af4538c | ||
|
|
a0981cb363 | ||
|
|
894de992da | ||
|
|
ca1bbaa7ed | ||
|
|
2ce98bec12 | ||
|
|
3db24b1b40 | ||
|
|
0e61a94b5f | ||
|
|
194f2a56b7 | ||
|
|
efe05b9b1a | ||
|
|
8e2478f964 | ||
|
|
91600c12eb | ||
|
|
35aef0486f | ||
|
|
df6d58da30 | ||
|
|
942249395b | ||
|
|
500e952d5c | ||
|
|
1c56fe47c4 | ||
|
|
bbd33cfcc1 | ||
|
|
5083a29ccd | ||
|
|
6e063a5d23 | ||
|
|
87b11c227b | ||
|
|
a3f6d5b971 | ||
|
|
db10fd292a | ||
|
|
06c485bb3e | ||
|
|
5546250963 | ||
|
|
072147289b | ||
|
|
5b065ad7f2 | ||
|
|
cde2b05366 | ||
|
|
e8d1fc9b60 | ||
|
|
60771f2f27 | ||
|
|
dbe2c6862c | ||
|
|
35442a2004 | ||
|
|
1a4c7eb027 | ||
|
|
60a9893d52 | ||
|
|
1cd27605f0 | ||
|
|
8c2ffb9d26 | ||
|
|
08f41f6450 | ||
|
|
d184921e09 | ||
|
|
57e26f75b0 | ||
|
|
2eaf46c80d | ||
|
|
48a2648ec5 | ||
|
|
bfffb837d3 | ||
|
|
547fd02911 | ||
|
|
aa92498033 | ||
|
|
2737feaa2a | ||
|
|
988cc5b18d | ||
|
|
ae49f4be68 | ||
|
|
c51d05f1e1 | ||
|
|
c5ef67adcf | ||
|
|
c5f78ea58a | ||
|
|
d066b11e18 | ||
|
|
cb85a7b804 | ||
|
|
3dcba10659 | ||
|
|
4c6be0bc6f | ||
|
|
7f4d7b8f4e | ||
|
|
024576620a | ||
|
|
5b670bda3f | ||
|
|
6b0e135bdb | ||
|
|
204b9df2d6 | ||
|
|
8b49379f87 | ||
|
|
d32a00fb2f | ||
|
|
59c9ca70e4 | ||
|
|
4d1cd791f3 | ||
|
|
6529372830 | ||
|
|
cb2d4bde47 | ||
|
|
d66d799387 | ||
|
|
ca43ab0c61 | ||
|
|
1c60d5946b | ||
|
|
c9768e34bc | ||
|
|
51f205b273 | ||
|
|
2352f7be6b | ||
|
|
0161ad9d92 | ||
|
|
3589479481 | ||
|
|
e9acfeccda | ||
|
|
244db1b571 | ||
|
|
75795809ef | ||
|
|
825a5a7f2e | ||
|
|
4ed8f16325 | ||
|
|
ec896295d6 | ||
|
|
ad7cf12cd5 | ||
|
|
2375135f9a | ||
|
|
a4800f42dd | ||
|
|
1e48c7e6d0 | ||
|
|
88456472dc | ||
|
|
7040fe65da | ||
|
|
9646ce4d1a | ||
|
|
6df83bd190 | ||
|
|
10d975d0e7 | ||
|
|
f46a81bf3a | ||
|
|
460e5ba483 | ||
|
|
bae772c6f1 | ||
|
|
abdb023ae3 | ||
|
|
79e0bc1ab2 | ||
|
|
847cf4a646 | ||
|
|
bdb3f39ffa | ||
|
|
803e2bd5c9 | ||
|
|
2f7ba402dd | ||
|
|
26374d9be8 | ||
|
|
7ae7690846 | ||
|
|
c21c6cb00b | ||
|
|
1bd9dd2e5d | ||
|
|
bc0ec2e717 | ||
|
|
31409edb7f | ||
|
|
ed66371ebd | ||
|
|
5727331966 | ||
|
|
40039a6897 | ||
|
|
34593dccac | ||
|
|
248ab78e77 | ||
|
|
a05d4e1852 | ||
|
|
6b9bdb11df | ||
|
|
505d2e5123 | ||
|
|
29c2442e47 | ||
|
|
fcb78b81ec | ||
|
|
92ab53dec6 | ||
|
|
c2475e43fd | ||
|
|
5cd3ec521b | ||
|
|
232853214d | ||
|
|
47cff96e2e | ||
|
|
d1a9c6e62d | ||
|
|
8f0b57138b | ||
|
|
3167a1c98b | ||
|
|
a024fb10bc | ||
|
|
531e8d7ad9 | ||
|
|
5293b9694d | ||
|
|
9008d5a050 | ||
|
|
9488637956 | ||
|
|
c18f3ba6f9 | ||
|
|
f429737c12 | ||
|
|
04fd683e9c | ||
|
|
4b9f46a6b0 | ||
|
|
793a5b6d60 | ||
|
|
4e6e9a13b6 | ||
|
|
010f455382 | ||
|
|
acebc322b8 | ||
|
|
6f280d81b5 | ||
|
|
d48af86cdf | ||
|
|
25ec78df9a | ||
|
|
90353c437e | ||
|
|
8c78387136 | ||
|
|
fbb50af208 | ||
|
|
8684d9d5d1 | ||
|
|
2375953e95 | ||
|
|
ecbb196551 | ||
|
|
90ca758a98 | ||
|
|
93d674a7a2 | ||
|
|
03ec3f739b | ||
|
|
a3eb9c8e6b | ||
|
|
30f3e123cd | ||
|
|
4d4c2325f9 | ||
|
|
db84abedfc | ||
|
|
2609e3af6e | ||
|
|
bd4f07f6cb | ||
|
|
6f72e7f752 | ||
|
|
ddde1ff8d4 | ||
|
|
5b917af59b | ||
|
|
7c09989768 | ||
|
|
7794557c18 | ||
|
|
495cb138ee | ||
|
|
b177b2f0dc | ||
|
|
f956c6b2de | ||
|
|
62dbc570b2 | ||
|
|
b494d821b1 | ||
|
|
f63b033c72 | ||
|
|
4d782b8fad | ||
|
|
fc1bd60e49 | ||
|
|
c221f5a29d | ||
|
|
112a9871ee | ||
|
|
1c1ffa875e | ||
|
|
3f50a6f4ce | ||
|
|
1e61c5e366 | ||
|
|
ee0821d62f | ||
|
|
9cd037dfdd | ||
|
|
aaa828472f | ||
|
|
15443a32a1 | ||
|
|
9360abf011 | ||
|
|
7475a13e6a | ||
|
|
9d2df07b71 | ||
|
|
7f80725ddc | ||
|
|
bc9ba8c8da | ||
|
|
7718b37ecf | ||
|
|
10efeb79d1 | ||
|
|
bdd9db4892 | ||
|
|
c0f61c5cb4 | ||
|
|
85a4d88e2a | ||
|
|
fd7efca993 | ||
|
|
742db7c701 | ||
|
|
9a47796ea3 | ||
|
|
f724cf348a | ||
|
|
54cb56988b | ||
|
|
09dddd6f11 | ||
|
|
91590f39c3 | ||
|
|
161278cd30 | ||
|
|
fe3251bf02 | ||
|
|
da4f94e9ef | ||
|
|
628818b2f1 | ||
|
|
c9cf8d080d | ||
|
|
1e7f897efb | ||
|
|
46de4f5911 | ||
|
|
e51ea52ca4 | ||
|
|
003afb8a4c | ||
|
|
ce8cd01cfd | ||
|
|
a60baf8ce6 | ||
|
|
96bba18449 | ||
|
|
2a013af660 | ||
|
|
2bf7cff7b4 | ||
|
|
fa41baa1c8 | ||
|
|
6558294943 | ||
|
|
85b8bfb57d | ||
|
|
f9cce3ab62 | ||
|
|
292700bb52 | ||
|
|
fd2f0df34f | ||
|
|
4ac41a6427 | ||
|
|
58826d64c9 | ||
|
|
7714f8f42c | ||
|
|
a126203ccd | ||
|
|
6656a7ef72 | ||
|
|
1c1f622b7d | ||
|
|
16f1935531 | ||
|
|
a402b4e9b2 | ||
|
|
d269b25c17 | ||
|
|
7573b7c9fa | ||
|
|
56dfb0e67f | ||
|
|
3474158bf6 | ||
|
|
93a2f318e0 | ||
|
|
4a5ae97be5 | ||
|
|
186105c664 | ||
|
|
8df6973bc7 | ||
|
|
2cc76c588b | ||
|
|
8096c493e8 | ||
|
|
c0fe8c599b | ||
|
|
7cda1566e2 | ||
|
|
e83191f54e | ||
|
|
985dfe2749 | ||
|
|
e275737aa9 | ||
|
|
91b4825cca | ||
|
|
4c0a122240 | ||
|
|
db4060cf77 | ||
|
|
34addd59c5 | ||
|
|
8caddf1738 | ||
|
|
d9ca28ba43 | ||
|
|
a331a1951b | ||
|
|
336f1f8f35 | ||
|
|
8804df317d | ||
|
|
95408b0fae | ||
|
|
79819a5563 | ||
|
|
d57fc7c0d9 | ||
|
|
9fb590d79d | ||
|
|
cb4ca9228f |
@@ -369,6 +369,7 @@ miniapps/shifted/lsf_integral
|
||||
miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
miniapps/tools/convert-dc
|
||||
miniapps/tools/compare-dc
|
||||
miniapps/tools/gridfunction-bounds
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
|
||||
@@ -17,11 +17,19 @@ Discretization improvements
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behaviour has not changed.
|
||||
|
||||
- Added methods to estimate function extremum using piecewise linear bounds +
|
||||
recursive subdivision.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
@@ -106,6 +114,23 @@ Linear and nonlinear solvers
|
||||
Filtering (AMGF), providing robust preconditioning for linear systems arising
|
||||
in constrained optimization problems such as frictionless contact.
|
||||
|
||||
Added 'GetResiduals' and 'GetFinalAbsResidualNorm' to 'HyprePCG',
|
||||
'HypreGMRES', and 'HypreFGMRES' to get 'r' and '|r|_p'. Note that the latter
|
||||
computes '|r|_p' from 'r' instead of returning a cached value like the
|
||||
relative 'GetFinalResidualNorm'. These require Hypre >= 2.15.0.
|
||||
|
||||
Changed the default solver parameters for 'HyprePCG' to 'tol=1e-6' and
|
||||
'max_iter=1000'. This matches the default parameters in Hypre 3.0.
|
||||
|
||||
Added various helper functions for querying/modifying Hypre solvers:
|
||||
'HypreSmoother::GetType', 'HypreSmoother::GetSOROptions',
|
||||
'HypreSmoother::GetPolyOptions', 'HypreSmoother::GetWindowParameters',
|
||||
'HypreSmoother::IsOperatorSymmetric', 'HyprePCG::GetTol',
|
||||
'HyprePCG::GetAbsTol', 'HyprePCG::GetMaxIter', 'HyprePCG::SetUseTwoNorm',
|
||||
'HypreGMRES::GetTol', 'HypreGMRES::GetAbsTol', 'HypreGMRES::GetMaxIter',
|
||||
'HypreGMRES::GetKDim', 'HypreFGMRES::GetTol', 'HypreFGMRES::GetMaxIter',
|
||||
'HypreFGMRES::GetKDim', and 'HypreBoomerAMG::GetMaxIter'.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added the 'gpu', 'raja-gpu', and 'ceed-gpu' backend aliases/shortcuts which
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# Some choices below are based on the OS type:
|
||||
NOTMAC := $(subst Darwin,,$(shell uname -s))
|
||||
|
||||
ASTYLE_BIN = astyle
|
||||
ETAGS_BIN = $(shell command -v etags 2> /dev/null)
|
||||
EGREP_BIN = $(shell command -v egrep 2> /dev/null)
|
||||
|
||||
|
||||
+5
-1
@@ -9,6 +9,7 @@
|
||||
// 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.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-q2.vtk
|
||||
// ex4 -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// ex4 -m ../data/square-disc-nurbs.mesh
|
||||
@@ -18,6 +19,7 @@
|
||||
// ex4 -m ../data/amr-quad.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/ref-prism.mesh -o 1
|
||||
// ex4 -m ../data/octahedron.mesh -o 1
|
||||
@@ -25,6 +27,8 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex4 -m ../data/star.mesh -pa -d cuda
|
||||
// ex4 -m ../data/star.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/amr-quad.mesh -hb -ea -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
|
||||
@@ -193,7 +197,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
if (!pa && (!ea || hybridization))
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
|
||||
+6
-1
@@ -9,6 +9,7 @@
|
||||
// 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.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
|
||||
@@ -17,14 +18,18 @@
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
|
||||
// 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 -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -ea -hb -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
|
||||
@@ -230,7 +235,7 @@ int main(int argc, char *argv[])
|
||||
pcg->SetMaxIter(2000);
|
||||
pcg->SetPrintLevel(1);
|
||||
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
|
||||
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else if (pa || ea) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
|
||||
+35
-6
@@ -825,14 +825,46 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
Vector &b, OperatorHandle &A, Vector &X,
|
||||
Vector &B, int copy_interior)
|
||||
{
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
if (ext)
|
||||
{
|
||||
if (hybridization)
|
||||
{
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
ConstrainedOperator A_constrained(this, ess_tdof_list);
|
||||
A_constrained.EliminateRHS(x, b);
|
||||
hybridization->ReduceRHS(b, B);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
Vector conf_b, conf_x;
|
||||
if (P)
|
||||
{
|
||||
// Nonconforming
|
||||
conf_b.SetSize(P->Width());
|
||||
conf_x.SetSize(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conforming
|
||||
conf_b.MakeRef(b, 0, b.Size());
|
||||
conf_x.MakeRef(x, 0, x.Size());
|
||||
}
|
||||
|
||||
A_constrained->EliminateRHS(conf_x, conf_b);
|
||||
|
||||
if (P)
|
||||
{
|
||||
R->MultTranspose(conf_b, b); // store eliminated rhs in b
|
||||
}
|
||||
|
||||
hybridization->ReduceRHS(conf_b, B);
|
||||
X.SetSize(B.Size());
|
||||
X = 0.0;
|
||||
}
|
||||
@@ -842,7 +874,6 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
}
|
||||
return;
|
||||
}
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
// Transform the system and perform the elimination in B, based on the
|
||||
@@ -878,7 +909,6 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
if (hybridization)
|
||||
{
|
||||
// Reduction to the Lagrange multipliers system
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
Vector conf_b(P->Width()), conf_x(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
@@ -891,7 +921,6 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
else
|
||||
{
|
||||
// Variational restriction with P
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
B.SetSize(P->Width());
|
||||
P->MultTranspose(b, B);
|
||||
X.SetSize(R->Height());
|
||||
|
||||
+67
-28
@@ -39,8 +39,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
b_type = b_type_i;
|
||||
cp_type = cp_type_i;
|
||||
tol = tol_i;
|
||||
lbound.SetSize(nb, ncp);
|
||||
ubound.SetSize(nb, ncp);
|
||||
lbound.SetSize(ncp, nb);
|
||||
ubound.SetSize(ncp, nb);
|
||||
nodes.SetSize(nb);
|
||||
weights.SetSize(nb);
|
||||
control_points.SetSize(ncp);
|
||||
@@ -125,21 +125,25 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
}
|
||||
else if (j == ncp-1)
|
||||
{
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
vals(0) = bv(i);
|
||||
vals(1) = bmv(i) + dm*bdmv(i);
|
||||
vals(2) = bpv(i) + dp*bdpv(i);
|
||||
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
|
||||
lbound(j,i) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(j,i) = vals.Max()+tol; // tolerance for good measure
|
||||
if (b_type == 2)
|
||||
{
|
||||
lbound(j,i) = std::max(lbound(j,i),0_r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,8 +277,7 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
intmax.SetSize(ncp);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector coeffm(nb);
|
||||
coeffm = 0.0;
|
||||
Vector coeffm;
|
||||
|
||||
real_t a0 = 0.0;
|
||||
real_t a1 = 0.0;
|
||||
@@ -302,6 +305,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
// compute L2 projection for linear bases: a0 + a1*x
|
||||
if (proj)
|
||||
{
|
||||
coeffm.SetSize(nb);
|
||||
coeffm = 0.0;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1;
|
||||
@@ -342,8 +347,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t c = coeffm(i);
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmin(j) += min(lbound(j,i)*c, ubound(j,i)*c);
|
||||
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,10 +479,10 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth row
|
||||
{
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
intmin(k*ncp+i) += vals.Min();
|
||||
intmax(k*ncp+i) += vals.Max();
|
||||
}
|
||||
@@ -553,17 +558,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1; // x-coordinate
|
||||
minBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
maxBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
minNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
}
|
||||
// Compute Bernstein coefficients
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, minBounds.GetData());
|
||||
lu.Solve(nb, 1, maxBounds.GetData());
|
||||
lu.Solve(nb, 1, minNodalVals.GetData());
|
||||
lu.Solve(nb, 1, maxNodalVals.GetData());
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
intminT(i*ncp2+j) = minBounds(i);
|
||||
intmaxT(i*ncp2+j) = maxBounds(i);
|
||||
intminT(i*ncp2+j) = minNodalVals(i);
|
||||
intmaxT(i*ncp2+j) = maxNodalVals(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -617,10 +622,10 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth slice
|
||||
{
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
intmin(k*ncp2+i) += vals.Min();
|
||||
intmax(k*ncp2+i) += vals.Max();
|
||||
}
|
||||
@@ -653,7 +658,8 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
Vector &nodesBern) const
|
||||
{
|
||||
const int nbern = nodesBern.Size();
|
||||
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
|
||||
L2_SegmentElement el(nbern-1, 2);
|
||||
// we use L2 to leverage lexicographic order
|
||||
Array<int> ordering = el.GetLexicographicOrdering();
|
||||
basisMat.SetSize(nbern, nbern);
|
||||
Vector shape(nbern);
|
||||
@@ -666,6 +672,39 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetBoundingMatrix(int dim, bool is_lower) const
|
||||
{
|
||||
if (dim > 1)
|
||||
{
|
||||
const int ncpd = static_cast<int>(std::pow(ncp, dim));
|
||||
const int nbd = static_cast<int>(std::pow(nb, dim));
|
||||
DenseMatrix boundND(ncpd, nbd);
|
||||
Vector phimin, phimax, col;
|
||||
Vector coeffs(nbd);
|
||||
coeffs = 0.0;
|
||||
for (int j = 0; j < nbd; j++)
|
||||
{
|
||||
coeffs(j) = 1.0;
|
||||
boundND.GetColumnReference(j, col);
|
||||
GetNDBounds(dim, coeffs, phimin, phimax);
|
||||
col = is_lower ? phimin : phimax;
|
||||
coeffs(j) = 0.0;
|
||||
}
|
||||
return boundND;
|
||||
}
|
||||
return is_lower ? lbound : ubound;
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetLowerBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, true);
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetUpperBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, false);
|
||||
}
|
||||
|
||||
constexpr int PLBound::min_ncp_gl_x[2][11];
|
||||
constexpr int PLBound::min_ncp_gll_x[2][11];
|
||||
constexpr int PLBound::min_ncp_pos_x[2][11];
|
||||
@@ -716,4 +755,4 @@ void PLBound::Print(std::ostream &outp) const
|
||||
ubound.Print(outp);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+71
-20
@@ -19,14 +19,18 @@ namespace mfem
|
||||
{
|
||||
|
||||
/** @name Piecewise linear bounds of bases
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on
|
||||
the grid function in each element. The bounds for the bases are constructed
|
||||
based on the following parameters:
|
||||
|
||||
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
|
||||
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre
|
||||
nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
2 - Positive/Bernstein bases on uniformly distributed nodes,
|
||||
|
||||
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
|
||||
(iii) @b ncp: number of control points used to construct the piecewise
|
||||
linear bounds
|
||||
|
||||
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
|
||||
1 - Chebyshev.
|
||||
@@ -35,7 +39,9 @@ namespace mfem
|
||||
|
||||
If the user does not specify @b ncp and @b cp_type, the minimum value of
|
||||
@b ncp is used that would bound the bases for the @b cp_type. We default
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases.
|
||||
Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and
|
||||
increasing @b ncp results in tighter bounds.
|
||||
|
||||
Finally, only tensor-product elements are currently supported.
|
||||
|
||||
@@ -54,7 +60,7 @@ private:
|
||||
bool proj = true; // Use linear projection to compute bounds.
|
||||
real_t tol = 0.0; // offset bounds to avoid round-off errors
|
||||
Vector nodes, weights, control_points;
|
||||
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
|
||||
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
|
||||
// Some auxillary storage for computing the bounds with Bernstein
|
||||
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
|
||||
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
|
||||
@@ -80,6 +86,9 @@ private:
|
||||
{3,5,8,9,11,12,13,13,14,15,16}
|
||||
};
|
||||
|
||||
/// Helper function to extract lower or upper bounding matrix
|
||||
DenseMatrix GetBoundingMatrix(int dim, bool is_lower) const;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
@@ -92,40 +101,82 @@ public:
|
||||
PLBound(const FiniteElementSpace *fes,
|
||||
const int ncp_i = -1, const int cp_type_i = 0);
|
||||
|
||||
// Get minimum number of control points needed to bound the given bases
|
||||
/// Get minimum number of control points needed to bound the given bases
|
||||
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
int cp_type_i) const;
|
||||
|
||||
// Print information about the bounds
|
||||
/// Print information about the bounds
|
||||
void Print(std::ostream &outp = mfem::out) const;
|
||||
|
||||
// Enable (default) or disable linear projection before bounding.
|
||||
// This projection increases the computational cost but results in tighter
|
||||
// bounds.
|
||||
/** @brief Enable (default) or disable linear projection before bounding.
|
||||
*
|
||||
* @details This projection increases the computational cost but results in
|
||||
* tighter bounds.
|
||||
*/
|
||||
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D/2D/3D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D/2D/3D.
|
||||
*
|
||||
* @param[in] rdim The spatial dimension of the element (1, 2, or 3).
|
||||
* @param[in] coeff The vector of lexicographically-ordered coefficients.
|
||||
* Should be of size nb^rdim, where nb is the number of
|
||||
* bases/nodes in 1D. These coefficients must correspond
|
||||
* to the bases type and number of bases, used in the
|
||||
* constructor of PLBound.
|
||||
*
|
||||
* @param[out] intmin The vector of minimum bound for all control points.
|
||||
* @param[out] intmax The vector of maximum bound for all control points.
|
||||
* Both intmin and intmax are of size ncp^rdim, where
|
||||
* ncp is the number of control points in 1D, and are
|
||||
* ordered lexicographically.
|
||||
*/
|
||||
void GetNDBounds(const int rdim, const Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Get number of control points used to compute the bounds.
|
||||
int GetNControlPoints() const { return ncp; }
|
||||
|
||||
/// Get 1D control point locations (lexicographic order) in [0,1].
|
||||
const Vector &GetControlPoints() const { return control_points; }
|
||||
|
||||
/** @brief Get lower and upper bounding matrix (ncp^dim x nb^dim)
|
||||
*
|
||||
* @details The matrices can be used to compute the bounds at control points
|
||||
* by a simple matrix-vector product with the
|
||||
* lexicographically-ordered nodal coefficients.
|
||||
* The resulting output is also lexicographically-ordered.
|
||||
*
|
||||
* @note These matrices do not account for the linear projection step that
|
||||
* is optionally done in GetNDBounds before bounding the function.
|
||||
*/
|
||||
///@{
|
||||
DenseMatrix GetLowerBoundMatrix(int dim = 1) const;
|
||||
DenseMatrix GetUpperBoundMatrix(int dim = 1) const;
|
||||
///@}
|
||||
|
||||
private:
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 2D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 2D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 3D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 3D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
/// for Bernstein bases.
|
||||
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
* for Bernstein bases.
|
||||
*/
|
||||
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
|
||||
|
||||
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
|
||||
+215
-41
@@ -84,29 +84,33 @@ public:
|
||||
const std::vector<derivative_action_t> &derivative_actions,
|
||||
const FieldDescriptor &direction,
|
||||
const int &daction_l_size,
|
||||
const std::vector<derivative_action_t> &derivative_actions_transpose,
|
||||
const int &derivative_action_tr_l_size,
|
||||
const std::vector<derivative_action_t> &derivative_tr_actions,
|
||||
const FieldDescriptor &transpose_direction,
|
||||
const int &daction_transpose_l_size,
|
||||
const std::vector<Vector *> &solutions_l,
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::function<void(Vector &, Vector &)> &tr_prolongation_transpose,
|
||||
const std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
&assemble_derivative_sparsematrix_callbacks,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
const assemble_derivative_hypreparmatrix_callback_t
|
||||
&assemble_derivative_hypreparmatrix_callback) :
|
||||
Operator(height, width),
|
||||
derivative_actions(derivative_actions),
|
||||
direction(direction),
|
||||
daction_l(daction_l_size),
|
||||
daction_l_size(daction_l_size),
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
derivative_action_tr_l_size(derivative_action_tr_l_size),
|
||||
derivative_tr_actions(derivative_tr_actions),
|
||||
transpose_direction(transpose_direction),
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
tr_prolongation_transpose(tr_prolongation_transpose),
|
||||
assemble_derivative_sparsematrix_callbacks(
|
||||
assemble_derivative_sparsematrix_callbacks),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
assemble_derivative_hypreparmatrix_callback(
|
||||
assemble_derivative_hypreparmatrix_callback)
|
||||
{
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
@@ -156,18 +160,18 @@ public:
|
||||
/// direction_t on T-dofs.
|
||||
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
|
||||
{
|
||||
MFEM_ASSERT(!derivative_actions_transpose.empty(),
|
||||
MFEM_ASSERT(!derivative_tr_actions.empty(),
|
||||
"derivative can't be used to be multiplied in transpose mode");
|
||||
|
||||
daction_l.SetSize(width);
|
||||
daction_l.SetSize(derivative_action_tr_l_size);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(transpose_direction, direction_t, direction_l);
|
||||
for (const auto &f : derivative_actions_transpose)
|
||||
for (const auto &f : derivative_tr_actions)
|
||||
{
|
||||
f(fields_e, direction_l, daction_l);
|
||||
}
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
tr_prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Assemble the derivative operator into a SparseMatrix.
|
||||
@@ -183,6 +187,10 @@ public:
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
|
||||
// SparseMatrix A is finalized after all callbacks have contributed to
|
||||
// it.
|
||||
A->Finalize();
|
||||
}
|
||||
|
||||
/// @brief Assemble the derivative operator into a HypreParMatrix.
|
||||
@@ -191,13 +199,7 @@ public:
|
||||
/// be an uninitialized object.
|
||||
void Assemble(HypreParMatrix *&A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a HypreParMatrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
assemble_derivative_hypreparmatrix_callback(fields_e, A);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -212,10 +214,12 @@ private:
|
||||
|
||||
const int daction_l_size;
|
||||
|
||||
const int derivative_action_tr_l_size;
|
||||
|
||||
/// Transpose Derivative action callbacks. Depending on the requested
|
||||
/// derivatives in DifferentiableOperator the callbacks represent certain
|
||||
/// combinations of actions of derivatives of the forward operator.
|
||||
std::vector<derivative_action_t> derivative_actions_transpose;
|
||||
std::vector<derivative_action_t> derivative_tr_actions;
|
||||
|
||||
FieldDescriptor transpose_direction;
|
||||
|
||||
@@ -225,13 +229,15 @@ private:
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
std::function<void(Vector &, Vector &)> tr_prolongation_transpose;
|
||||
|
||||
/// Callbacks that assemble derivatives into a SparseMatrix.
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
|
||||
/// Callbacks that assemble derivatives into a HypreParMatrix.
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
assemble_derivative_hypreparmatrix_callback_t
|
||||
assemble_derivative_hypreparmatrix_callback;
|
||||
};
|
||||
|
||||
/// Class representing a differentiable operator which acts on solution and
|
||||
@@ -457,7 +463,10 @@ public:
|
||||
dir_l = s_l[derivative_idx];
|
||||
}
|
||||
|
||||
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
|
||||
for (size_t i = 0; i < derivative_setup_callbacks[derivative_id].size(); i++)
|
||||
{
|
||||
derivative_setup_callbacks[derivative_id][i](fields_e, dir_l);
|
||||
}
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
height,
|
||||
@@ -465,15 +474,17 @@ public:
|
||||
derivative_action_callbacks[derivative_id],
|
||||
fields[derivative_idx],
|
||||
residual_l.Size(),
|
||||
daction_transpose_callbacks[derivative_id],
|
||||
derivative_action_tr_l_size[derivative_id],
|
||||
derivative_action_tr_callbacks[derivative_id],
|
||||
fields[test_space_field_idx],
|
||||
GetVSize(fields[test_space_field_idx]),
|
||||
sol_l,
|
||||
par_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose,
|
||||
derivative_tr_prolongation_transpose[derivative_id],
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id],
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
assemble_derivative_hypreparmatrix_callback[derivative_id]);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -486,13 +497,15 @@ private:
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::vector<derivative_action_t>> derivative_action_tr_callbacks;
|
||||
std::map<size_t,
|
||||
std::function<void(Vector &, Vector &)>> derivative_tr_prolongation_transpose;
|
||||
std::map<size_t, int> derivative_action_tr_l_size;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
std::map<size_t, assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callback;
|
||||
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
@@ -510,7 +523,7 @@ private:
|
||||
std::function<void(Vector &, Vector &)> output_restriction_transpose;
|
||||
restriction_callback_t restriction_callback;
|
||||
|
||||
std::map<size_t, Vector> derivative_qp_caches;
|
||||
std::map<size_t, std::vector<Vector>> derivative_qp_caches;
|
||||
|
||||
std::map<size_t, size_t> assembled_vector_sizes;
|
||||
|
||||
@@ -769,9 +782,10 @@ void DifferentiableOperator::AddIntegrator(
|
||||
auto input_size_on_qp =
|
||||
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
|
||||
|
||||
// printf("calculate shmem action info\n");
|
||||
auto action_shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
|
||||
|
||||
Vector shmem_cache(action_shmem_info.total_size);
|
||||
@@ -894,9 +908,10 @@ void DifferentiableOperator::AddIntegrator(
|
||||
const int da_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
|
||||
|
||||
// printf("calculate shmem derivative action info\n");
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>(
|
||||
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
|
||||
input_dtq_maps, output_dtq_maps, fields, num_entities,
|
||||
num_qp, input_size_on_qp, residual_size_on_qp,
|
||||
element_dof_ordering, d_field_idx);
|
||||
|
||||
@@ -968,9 +983,12 @@ void DifferentiableOperator::AddIntegrator(
|
||||
// Quadrature point local derivative cache for each element, with data
|
||||
// layout:
|
||||
// [test_vdim, test_op_dim, trial_vdim, trial_op_dim, qp, num_entities].
|
||||
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
|
||||
trial_vdim *
|
||||
total_trial_op_dim * num_qp * num_entities);
|
||||
derivative_qp_caches[derivative_id].push_back(
|
||||
Vector(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim * num_qp *
|
||||
num_entities));
|
||||
|
||||
const int cache_index = this->derivative_qp_caches[derivative_id].size() - 1;
|
||||
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& fields_ref = this->fields;
|
||||
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
|
||||
@@ -1012,6 +1030,8 @@ void DifferentiableOperator::AddIntegrator(
|
||||
trial_vdim,
|
||||
inputs_trial_op_dim,
|
||||
|
||||
qpdc_idx = cache_index,
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref
|
||||
](std::vector<Vector> &f_e, const Vector &dir_l) mutable
|
||||
@@ -1024,7 +1044,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].ReadWrite(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
@@ -1095,6 +1115,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
inputs_trial_op_dim,
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
qpdc_idx = cache_index,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&or_transpose
|
||||
@@ -1112,7 +1133,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
@@ -1157,6 +1178,154 @@ void DifferentiableOperator::AddIntegrator(
|
||||
or_transpose(derivative_action_e, der_action_l);
|
||||
});
|
||||
|
||||
// This prevents Sum/Identity. These are invalid
|
||||
// as input FieldOperators anyways.
|
||||
constexpr auto dummy_fop = Value<0> {};
|
||||
|
||||
auto [input_rt,
|
||||
input_e_sz] = get_restriction_transpose<entity_t>
|
||||
(fields[d_field_idx],
|
||||
element_dof_ordering, dummy_fop);
|
||||
|
||||
const auto input_restriction_transpose = input_rt;
|
||||
|
||||
derivative_tr_prolongation_transpose[derivative_id] =
|
||||
get_prolongation_transpose(
|
||||
fields[d_field_idx], dummy_fop, mesh.GetComm());
|
||||
|
||||
const auto d_tr_field_idx = test_space_field_idx;
|
||||
const auto direction_tr = fields[d_tr_field_idx];
|
||||
|
||||
auto output_size_on_qp =
|
||||
get_input_size_on_qp(outputs, std::make_index_sequence<num_outputs> {});
|
||||
|
||||
const int residual_tr_size_on_qp = trial_vdim * total_trial_op_dim;
|
||||
|
||||
auto shmem_tr_info =
|
||||
get_shmem_info<entity_t, num_fields, num_outputs, num_inputs>(
|
||||
output_dtq_maps, input_dtq_maps, fields, num_entities,
|
||||
num_qp, output_size_on_qp, residual_tr_size_on_qp,
|
||||
element_dof_ordering, test_space_field_idx);
|
||||
|
||||
// print_shared_memory_info(shmem_tr_info);
|
||||
|
||||
// TODO: this is a hack to extend the shared memory with a known
|
||||
// offset for a temp variable
|
||||
Vector shmem_tr_cache(shmem_tr_info.total_size + residual_tr_size_on_qp *
|
||||
num_qp);
|
||||
|
||||
Vector direction_tr_e(get_restriction<entity_t>(
|
||||
fields[test_space_field_idx],
|
||||
element_dof_ordering)->Height());
|
||||
|
||||
derivative_action_tr_l_size[derivative_id] =
|
||||
get_restriction<entity_t>(fields[d_field_idx],
|
||||
element_dof_ordering)->Width();
|
||||
|
||||
Vector derivative_action_tr_e(input_e_sz);
|
||||
derivative_action_tr_e = 0.0;
|
||||
|
||||
derivative_action_tr_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_trial_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
outputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
// output_fop, // class derived from FieldOperator
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_tr_cache, // Vector (local)
|
||||
shmem_tr_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent,
|
||||
direction_tr, // FieldDescriptor
|
||||
direction_tr_e, // Vector
|
||||
derivative_action_tr_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
inputs_trial_op_dim,
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
input_restriction_transpose,
|
||||
qpdc_idx = cache_index,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_tr_l,
|
||||
Vector &derivative_action_tr_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction_tr, dir_tr_l, direction_tr_e,
|
||||
element_dof_ordering);
|
||||
auto ye = Reshape(derivative_action_tr_e.ReadWrite(), num_trial_dof,
|
||||
trial_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_tr_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_tr_e.ReadWrite(),
|
||||
shmem_tr_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_attr = attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
|
||||
derivative_action_tr_e = 0.0;
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [output_dtq_shmem, input_dtq_shmem, fields_shmem,
|
||||
direction_shmem, input_shmem,
|
||||
shadow_shmem_, residual_shmem,
|
||||
scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_tr_info, output_dtq_maps, input_dtq_maps,
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
std::array<bool, num_outputs> all_true{true};
|
||||
map_direction_to_quadrature_data_conditional(
|
||||
shadow_shmem, direction_shmem, output_dtq_shmem, outputs,
|
||||
ir_weights, scratch_shmem, all_true, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), trial_vdim,
|
||||
total_trial_op_dim, num_qp);
|
||||
|
||||
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
|
||||
constexpr bool transpose = true;
|
||||
apply_qpdc(fhat, shadow_shmem, qpdce, itod, q1d, dimension,
|
||||
use_sum_factorization, transpose);
|
||||
|
||||
auto y = Reshape(&ye(0, 0, e), num_trial_dof, trial_vdim);
|
||||
auto fi_shmem = Reshape(shmem + shmem_tr_info.total_size, trial_vdim,
|
||||
total_trial_op_dim, num_qp);
|
||||
|
||||
map_quadrature_data_to_fields_conditional(
|
||||
y, fhat, inputs, itod, input_dtq_shmem, scratch_shmem, fi_shmem,
|
||||
input_is_dependent, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_tr_info.total_size,
|
||||
shmem_tr_cache.ReadWrite());
|
||||
input_restriction_transpose(derivative_action_tr_e, derivative_action_tr_l);
|
||||
});
|
||||
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
@@ -1190,7 +1359,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
inputs_trial_op_dim,
|
||||
Ae_mem,
|
||||
output_to_field,
|
||||
|
||||
qpdc_idx = cache_index,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&fields = fields_ref
|
||||
@@ -1202,7 +1371,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
@@ -1250,7 +1419,10 @@ void DifferentiableOperator::AddIntegrator(
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
if (A == nullptr)
|
||||
{
|
||||
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
}
|
||||
|
||||
auto tmp = Reshape(Ae_mem.HostReadWrite(), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim, num_entities);
|
||||
@@ -1321,14 +1493,15 @@ void DifferentiableOperator::AddIntegrator(
|
||||
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
|
||||
}
|
||||
}
|
||||
A->Finalize();
|
||||
// Don't finalize here since multiple callbacks might contribute to the same matrix
|
||||
// A->Finalize() will be called after all callbacks have contributed
|
||||
});
|
||||
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& assemble_derivative_sparsematrix_callbacks_ref =
|
||||
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
|
||||
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
|
||||
assemble_derivative_hypreparmatrix_callback[derivative_id] =
|
||||
[
|
||||
input_is_dependent,
|
||||
input_to_field,
|
||||
@@ -1342,6 +1515,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
{
|
||||
f(f_e, spmat);
|
||||
}
|
||||
spmat->Finalize();
|
||||
|
||||
if (spmat == nullptr)
|
||||
{
|
||||
@@ -1395,7 +1569,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
trial_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
delete spmat;
|
||||
});
|
||||
};
|
||||
}, derivative_ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,4 +530,65 @@ void map_quadrature_data_to_fields(
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_conditional(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &op_dims,
|
||||
const std::array<DofToQuadMap, N> &dtqmaps,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const DeviceTensor<3> &fi_shmem,
|
||||
const std::array<bool, N> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
int offset = 0;
|
||||
for_constexpr<N>([&](auto i)
|
||||
{
|
||||
if (conditions[i])
|
||||
{
|
||||
[[maybe_unused]] const auto [K, unused, M] = f.GetShape();
|
||||
const int L = static_cast<int>(op_dims(static_cast<size_t>(i)));
|
||||
auto fi = Reshape(&fi_shmem(0, 0, 0), K, L, M);
|
||||
for (int k = 0; k < K; k++)
|
||||
{
|
||||
for (int l = 0; l < L; l++)
|
||||
{
|
||||
for (int m = 0; m < M; m++)
|
||||
{
|
||||
fi(k, l, m) = f(k, l + offset, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_1d(
|
||||
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("dimension not supported"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
map_quadrature_data_to_fields_impl(y, fi, get<i>(fops), dtqmaps[i]);
|
||||
}
|
||||
offset += L;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
|
||||
+12
-17
@@ -505,13 +505,13 @@ void map_field_to_quadrature_data(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
|
||||
template <typename field_operator_ts, size_t N, size_t M>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_fields_to_quadrature_data(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<size_t, num_inputs> &input_to_field,
|
||||
std::array<DeviceTensor<2>, N> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, M> &fields_e,
|
||||
const std::array<DofToQuadMap, N> &dtqmaps,
|
||||
const std::array<size_t, N> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
@@ -523,7 +523,7 @@ void map_fields_to_quadrature_data(
|
||||
// attached to them and we create a dummy field which is not accessed
|
||||
// inside the functions it is passed to.
|
||||
const auto dummy_field_weight = DeviceTensor<1>(nullptr, 0);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
for_constexpr<N>([&](auto i)
|
||||
{
|
||||
const DeviceTensor<1> &field_e =
|
||||
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
|
||||
@@ -549,12 +549,7 @@ void map_fields_to_quadrature_data(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("unsupported dimension");
|
||||
#endif
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("unsupported dimension"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -627,20 +622,20 @@ void map_fields_to_quadrature_data_conditional(
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t num_inputs, typename field_operator_ts>
|
||||
template <size_t N, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_direction_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
|
||||
std::array<DeviceTensor<2>, N> &directions_qp,
|
||||
const DeviceTensor<1> &direction_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<DofToQuadMap, N> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const std::array<bool, N> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
for_constexpr<N>([&](auto i)
|
||||
{
|
||||
if (conditions[i])
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@ protected:
|
||||
};
|
||||
|
||||
/// @brief Uniform parameter space
|
||||
class UniformParameterSpace : public ParameterSpace
|
||||
class UniformParameterSpace final : public ParameterSpace
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for a uniform parameter space
|
||||
|
||||
@@ -379,54 +379,102 @@ namespace detail
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param op_dims operator dimensions.
|
||||
/// If an operator is dependent, the value corresponds to the spatial dimension.
|
||||
/// Otherwise a zero indicates indepence on the variable.
|
||||
/// @param q the current quadrature point index.
|
||||
template <size_t num_fields>
|
||||
/// @param transpose switch to use transpose action.
|
||||
template <size_t N>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const std::array<DeviceTensor<2>, N> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q)
|
||||
const DeviceTensor<1, const real_t> &op_dims,
|
||||
const int &q,
|
||||
bool transpose)
|
||||
{
|
||||
const size_t num_ops = op_dims.GetShape()[0];
|
||||
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
const size_t num_inputs = itod.GetShape()[0];
|
||||
const int total_trial_op_dim = qpdc.GetShape()[3];
|
||||
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
|
||||
if (transpose)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
for (int m = 0; m < total_trial_op_dim; m++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(itod(s));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto d_qp =
|
||||
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
fhat(j, m, q) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we don't support more than output space right now
|
||||
// shadow_shmem will always be of size 1.
|
||||
constexpr int shadow_idx_tr = 0;
|
||||
auto d_qp = Reshape(&(shadow_shmem[shadow_idx_tr])[0], test_vdim, test_op_dim,
|
||||
num_qp);
|
||||
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_ops; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(op_dims(s));
|
||||
if (trial_op_dim == 0) { continue; }
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t contrib = qpdc(i, k, j, m + m_offset, q) * d_qp(i, k, q);
|
||||
sum += contrib;
|
||||
}
|
||||
}
|
||||
fhat(j, m + m_offset, q) += sum;
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_ops; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(op_dims(s));
|
||||
if (trial_op_dim == 0) { continue; }
|
||||
|
||||
const auto d_qp =
|
||||
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
fhat(i, k, q) = sum;
|
||||
}
|
||||
fhat(i, k, q) = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @brief Apply the quadrature point data cache (qpdc) to a vector
|
||||
/// (usually a direction).
|
||||
@@ -445,16 +493,18 @@ void apply_qpdc(
|
||||
/// @param q1d number of quadrature points in 1D.
|
||||
/// @param dimension spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
template <size_t num_fields>
|
||||
/// @param T switch to use transpose application.
|
||||
template <size_t N>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const std::array<DeviceTensor<2>, N> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
const bool &use_sum_factorization,
|
||||
const bool T = false)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
@@ -462,7 +512,7 @@ void apply_qpdc(
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
@@ -472,7 +522,7 @@ void apply_qpdc(
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -485,7 +535,7 @@ void apply_qpdc(
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -500,7 +550,7 @@ void apply_qpdc(
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +243,35 @@ void process_qf_arg(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i) = u(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * n) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
|
||||
@@ -327,20 +356,4 @@ void process_qf_result(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * n) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
|
||||
+9
-3
@@ -1202,7 +1202,14 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
|
||||
const Operator *P = get_prolongation(f);
|
||||
auto PT = [=](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
if (P)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
y = r_local;
|
||||
}
|
||||
};
|
||||
return PT;
|
||||
}
|
||||
@@ -1580,14 +1587,13 @@ struct SharedMemoryInfo
|
||||
std::array<int, 6> temp_sizes;
|
||||
};
|
||||
|
||||
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs, typename input_t>
|
||||
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs>
|
||||
SharedMemoryInfo<num_fields, num_inputs, num_outputs>
|
||||
get_shmem_info(
|
||||
const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
|
||||
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
|
||||
const std::vector<FieldDescriptor> &fields,
|
||||
const int &num_entities,
|
||||
const input_t &inputs,
|
||||
const int &num_qp,
|
||||
const std::vector<int> &input_size_on_qp,
|
||||
const int &residual_size_on_qp,
|
||||
|
||||
@@ -387,7 +387,7 @@ void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
|
||||
static constexpr int NB = Q1D ? Q1D : 1; // block size
|
||||
|
||||
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
|
||||
mfem::forall_2D<NB*NB>(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// Perform change of basis if needed
|
||||
if (CHANGE_BASIS)
|
||||
|
||||
@@ -69,9 +69,9 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index on a shared face, ordered lexicographically
|
||||
/// relative to element the element (where the local face is face_id), and
|
||||
/// return the corresponding face DOF index ordered lexicographically relative
|
||||
/// to the face itself.
|
||||
/// relative to the element (where the local face is face_id), return the
|
||||
/// corresponding face DOF index ordered lexicographically relative to the face
|
||||
/// itself.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace2D(const int face_id, const int orientation,
|
||||
const int size1d, const int index)
|
||||
|
||||
+55
-66
@@ -231,7 +231,7 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
|
||||
{
|
||||
for (int nd = 0; nd < dof; nd++)
|
||||
{
|
||||
Laplacian[nd] = hess(nd,0) + hess(nd,4) + hess(nd,5);
|
||||
Laplacian[nd] = hess(nd,0) + hess(nd,3) + hess(nd,5);
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
@@ -268,11 +268,9 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
|
||||
scale[0] = Gij(0,0);
|
||||
scale[1] = 2*Gij(0,1);
|
||||
scale[2] = 2*Gij(0,2);
|
||||
|
||||
scale[3] = 2*Gij(1,2);
|
||||
scale[4] = Gij(2,2);
|
||||
|
||||
scale[5] = Gij(1,1);
|
||||
scale[3] = Gij(1,1);
|
||||
scale[4] = 2*Gij(1,2);
|
||||
scale[5] = Gij(2,2);
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
@@ -309,12 +307,12 @@ void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
|
||||
map[2] = 2;
|
||||
|
||||
map[3] = 1;
|
||||
map[4] = 5;
|
||||
map[5] = 3;
|
||||
map[4] = 3;
|
||||
map[5] = 4;
|
||||
|
||||
map[6] = 2;
|
||||
map[7] = 3;
|
||||
map[8] = 4;
|
||||
map[7] = 4;
|
||||
map[8] = 5;
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
@@ -382,11 +380,7 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
if (!d2q)
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
@@ -661,58 +655,67 @@ void ScalarFiniteElement::ScalarLocalL2Restriction(
|
||||
void NodalFiniteElement::CreateLexicographicFullMap(const IntegrationRule &ir)
|
||||
const
|
||||
{
|
||||
// Get the FULL version of the map. This call contains omp critical region,
|
||||
// so it is done before the critical region below.
|
||||
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
// Get the FULL version of the map.
|
||||
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
|
||||
//Undo the native ordering which is what FiniteElement::GetDofToQuad returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
|
||||
const int b_dim = (range_type == VECTOR) ? dim : 1;
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
// Do not run if the new Dof2Quad is already present, e.g. added in a
|
||||
// previous call or added by another omp thread.
|
||||
if (DofToQuad::SearchArray(dof2quad_array, ir,
|
||||
DofToQuad::LEXICOGRAPHIC_FULL) == nullptr)
|
||||
{
|
||||
for (int d = 0; d < b_dim; d++)
|
||||
// Undo the native ordering which is what FiniteElement::GetDofToQuad
|
||||
// returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
|
||||
const int b_dim = (range_type == VECTOR) ? dim : 1;
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
for (int d = 0; d < b_dim; d++)
|
||||
{
|
||||
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
|
||||
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
|
||||
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
|
||||
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
|
||||
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int g_dim = [this]()
|
||||
{
|
||||
switch (deriv_type)
|
||||
const int g_dim = [this]()
|
||||
{
|
||||
case GRAD: return dim;
|
||||
case DIV: return 1;
|
||||
case CURL: return cdim;
|
||||
default: return 0;
|
||||
}
|
||||
}();
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int d = 0; d < g_dim; d++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
switch (deriv_type)
|
||||
{
|
||||
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
|
||||
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
|
||||
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
|
||||
case GRAD: return dim;
|
||||
case DIV: return 1;
|
||||
case CURL: return cdim;
|
||||
default: return 0;
|
||||
}
|
||||
}();
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int d = 0; d < g_dim; d++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
|
||||
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
|
||||
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q_new);
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q_new);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,13 +727,7 @@ const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
//Should make this loop a function of FiniteElement
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule == &ir && d2q->mode == mode) { break; }
|
||||
d2q = nullptr;
|
||||
}
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
}
|
||||
if (d2q) { return *d2q; }
|
||||
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
|
||||
@@ -2631,15 +2628,7 @@ const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
auto* d2q_ = dof2quad_array[i];
|
||||
if (d2q_->IntRule == &ir && d2q_->mode == mode)
|
||||
{
|
||||
d2q = d2q_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new DofToQuad;
|
||||
|
||||
@@ -222,6 +222,12 @@ public:
|
||||
|
||||
/// Returns absolute value of the maps
|
||||
DofToQuad Abs() const;
|
||||
|
||||
/// Auxiliary function for searching DofToQuad arrays.
|
||||
static inline DofToQuad *SearchArray(
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode);
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
@@ -407,6 +413,7 @@ public:
|
||||
/** Each row of the result DenseMatrix @a Hessian contains upper triangular
|
||||
part of the Hessian of one shape function.
|
||||
The order in 2D is {u_xx, u_xy, u_yy}.
|
||||
The order in 3D is {u_xx, u_xy, u_xz, u_yy, u_yz, u_zz}.
|
||||
The size (#dof x (#dim (#dim+1)/2) of @a Hessian must be set in advance.*/
|
||||
virtual void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &Hessian) const;
|
||||
@@ -1376,6 +1383,21 @@ public:
|
||||
void InvertLinearTrans(ElementTransformation &trans,
|
||||
const IntegrationPoint &pt, Vector &x);
|
||||
|
||||
|
||||
// static inline method
|
||||
inline DofToQuad *DofToQuad::SearchArray(
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode)
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
DofToQuad *d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule == &ir && d2q->mode == mode) { return d2q; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -60,6 +60,12 @@ void Linear1DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(1,0) = 1.;
|
||||
}
|
||||
|
||||
void Linear1DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
Linear2DFiniteElement::Linear2DFiniteElement()
|
||||
: NodalFiniteElement(2, Geometry::TRIANGLE, 3, 1)
|
||||
{
|
||||
@@ -87,6 +93,11 @@ void Linear2DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(2,0) = 0.; dshape(2,1) = 1.;
|
||||
}
|
||||
|
||||
void Linear2DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
BiLinear2DFiniteElement::BiLinear2DFiniteElement()
|
||||
: NodalFiniteElement(2, Geometry::SQUARE, 4, 1, FunctionSpace::Qk)
|
||||
@@ -1256,6 +1267,12 @@ void Linear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void Linear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
void Linear3DFiniteElement::GetFaceDofs (int face, int **dofs, int *ndofs)
|
||||
const
|
||||
{
|
||||
@@ -1632,6 +1649,37 @@ void TriLinear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(7,2) = ox * y;
|
||||
}
|
||||
|
||||
void TriLinear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
real_t x = ip.x, y = ip.y, z = ip.z;
|
||||
real_t ox = 1.-x, oy = 1.-y, oz = 1.-z;
|
||||
|
||||
h(0,0) = 0.; h(0,1) = oz; h(0,2) = oy;
|
||||
h(0,3) = 0.; h(0,4) = ox; h(0,5) = 0.;
|
||||
|
||||
h(1,0) = 0.; h(1,1) = -oz; h(1,2) = -oy;
|
||||
h(1,3) = 0.; h(1,4) = x; h(1,5) = 0.;
|
||||
|
||||
h(2,0) = 0.; h(2,1) = oz; h(2,2) = -y;
|
||||
h(2,3) = 0.; h(2,4) = -x; h(2,5) = 0.;
|
||||
|
||||
h(3,0) = 0.; h(3,1) = -oz; h(3,2) = y;
|
||||
h(3,3) = 0.; h(3,4) = -ox; h(3,5) = 0.;
|
||||
|
||||
h(4,0) = 0.; h(4,1) = z; h(4,2) = -oy;
|
||||
h(4,3) = 0.; h(4,4) = -ox; h(4,5) = 0.;
|
||||
|
||||
h(5,0) = 0.; h(5,1) = -z; h(5,2) = oy;
|
||||
h(5,3) = 0.; h(5,4) = -x; h(5,5) = 0.;
|
||||
|
||||
h(6,0) = 0.; h(6,1) = z; h(6,2) = y;
|
||||
h(6,3) = 0.; h(6,4) = x; h(6,5) = 0.;
|
||||
|
||||
h(7,0) = 0.; h(7,1) = -z; h(7,2) = -y;
|
||||
h(7,3) = 0.; h(7,4) = ox; h(7,5) = 0.;
|
||||
}
|
||||
|
||||
|
||||
P0SegmentFiniteElement::P0SegmentFiniteElement(int Ord)
|
||||
: NodalFiniteElement(1, Geometry::SEGMENT, 1, Ord) // default Ord = 0
|
||||
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
contains the derivative of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
};
|
||||
|
||||
/// A 2D linear element on triangle with nodes at the vertices of the triangle
|
||||
@@ -70,6 +72,8 @@ public:
|
||||
so that each row contains the derivatives of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
};
|
||||
@@ -404,6 +408,9 @@ public:
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
|
||||
@@ -445,7 +452,8 @@ public:
|
||||
so that each row contains the derivatives of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
};
|
||||
|
||||
+1
-1
@@ -589,7 +589,7 @@ void H1_TriangleElement::CalcHessian(const IntegrationPoint &ip,
|
||||
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
|
||||
Vector dshape_x(p + 1), dshape_y(p + 1), dshape_l(p + 1);
|
||||
Vector ddshape_x(p + 1), ddshape_y(p + 1), ddshape_l(p + 1);
|
||||
DenseMatrix ddu(dof, dim);
|
||||
DenseMatrix ddu(dof, (dim*(dim+1))/2);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x, ddshape_x);
|
||||
|
||||
+3
-4
@@ -445,11 +445,10 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
d2sum[0] += ( hessian(o,0) = d2sx*sy*sz*weights(o) );
|
||||
d2sum[1] += ( hessian(o,1) = dsx*dsy*sz*weights(o) );
|
||||
d2sum[2] += ( hessian(o,2) = dsx*sy*dsz*weights(o) );
|
||||
d2sum[3] += ( hessian(o,3) = sx*d2sy*sz*weights(o) );
|
||||
d2sum[4] += ( hessian(o,4) = sx*dsy*dsz*weights(o) );
|
||||
d2sum[5] += ( hessian(o,5) = sx*sy*d2sz*weights(o) );
|
||||
|
||||
d2sum[3] += ( hessian(o,3) = sx*dsy*dsz*weights(o) );
|
||||
|
||||
d2sum[4] += ( hessian(o,4) = sx*sy*d2sz*weights(o) );
|
||||
d2sum[5] += ( hessian(o,5) = sx*d2sy*sz*weights(o) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-32
@@ -282,14 +282,7 @@ int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
|
||||
void FiniteElementSpace::AdjustVDofs(Array<int> &vdofs)
|
||||
{
|
||||
int n = vdofs.Size(), *vdof = vdofs;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int j;
|
||||
if ((j = vdof[i]) < 0)
|
||||
{
|
||||
vdof[i] = -1-j;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) { vdof[i] = UnsignIndex(vdof[i]); }
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
@@ -483,13 +476,14 @@ void FiniteElementSpace::ReorderElementToDofTable()
|
||||
for (int k = 0, dof_counter = 0; k < nnz; k++)
|
||||
{
|
||||
const int sdof = J[k]; // signed dof
|
||||
const int dof = (sdof < 0) ? -1-sdof : sdof;
|
||||
const int dof = UnsignIndex(sdof);
|
||||
int new_dof = dof_marker[dof];
|
||||
if (new_dof < 0)
|
||||
{
|
||||
dof_marker[dof] = new_dof = dof_counter++;
|
||||
}
|
||||
J[k] = (sdof < 0) ? -1-new_dof : new_dof; // preserve the sign of sdof
|
||||
// Preserve the sign of sdof
|
||||
J[k] = (sdof < 0) ? FlipIndexSign(new_dof) : new_dof;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +541,7 @@ void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
|
||||
{
|
||||
for (auto d : dofs)
|
||||
{
|
||||
mark_array[d >= 0 ? d : -1 - d] = -1;
|
||||
mark_array[UnsignIndex(d)] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -931,7 +925,7 @@ void FiniteElementSpace::AddDependencies(
|
||||
if (std::abs(coef) > 1e-12)
|
||||
{
|
||||
const int mdof = master_dofs[j];
|
||||
if (mdof != sdof && mdof != (-1-sdof))
|
||||
if (mdof != sdof && mdof != FlipIndexSign(sdof))
|
||||
{
|
||||
deps.Add(sdof, mdof, coef);
|
||||
}
|
||||
@@ -1024,7 +1018,7 @@ int FiniteElementSpace::GetDegenerateFaceDofs(int index, Array<int> &dofs,
|
||||
// FiniteElementSpace::AddDependencies.
|
||||
|
||||
Array<int> edof;
|
||||
int order = GetEdgeDofs(-1 - index, edof, variant);
|
||||
int order = GetEdgeDofs(FlipIndexSign(index), edof, variant);
|
||||
|
||||
int nv = fec->DofForGeometry(Geometry::POINT);
|
||||
int ne = fec->DofForGeometry(Geometry::SEGMENT);
|
||||
@@ -1516,36 +1510,76 @@ const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
|
||||
const bool is_dg_space = IsDGSpace();
|
||||
const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
|
||||
L2FaceValues::DoubleValued : L2FaceValues::SingleValued;
|
||||
key_face key = std::make_tuple(is_dg_space, f_ordering, type, m);
|
||||
auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
|
||||
auto itr = L2F.find(key);
|
||||
if (itr != L2F.end())
|
||||
{
|
||||
return itr->second;
|
||||
return itr->second.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
FaceRestriction *res;
|
||||
std::unique_ptr<FaceRestriction> res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res = new L2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new L2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new NCL2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new NCL2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
|
||||
{
|
||||
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ConformingFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
L2F[key] = res;
|
||||
return res;
|
||||
return L2F.emplace(key, std::move(res)).first->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
const InterpolationManager &FiniteElementSpace::GetInterpolationManager(
|
||||
ElementDofOrdering f_ordering, FaceType type) const
|
||||
{
|
||||
const auto key = make_tuple(f_ordering, type);
|
||||
|
||||
auto it = interpolations.find(key);
|
||||
if (it != interpolations.end())
|
||||
{
|
||||
return *it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto interp = make_unique<InterpolationManager>(*this, f_ordering, type);
|
||||
|
||||
int face_idx = 0;
|
||||
for (int f = 0; f < mesh->GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation face = mesh->GetFaceInformation(f);
|
||||
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (face.IsConforming() || face.IsBoundary())
|
||||
{
|
||||
interp->RegisterFaceConformingInterpolation(face, face_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
interp->RegisterFaceCoarseToFineInterpolation(face, face_idx);
|
||||
}
|
||||
++face_idx;
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into contiguous memory.
|
||||
interp->LinearizeInterpolatorMapIntoVector();
|
||||
interp->InitializeNCInterpConfig();
|
||||
|
||||
return *interpolations.emplace(key, std::move(interp)).first->second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1670,8 +1704,8 @@ SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
|
||||
|
||||
for (int i = 0; i < fine_ldof; i++)
|
||||
{
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (!mark[m])
|
||||
{
|
||||
@@ -1732,7 +1766,7 @@ SparseMatrix *FiniteElementSpace::VariableOrderRefinementMatrix(
|
||||
for (int i = 0; i < fine_ldof; i++)
|
||||
{
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (!mark[m])
|
||||
{
|
||||
@@ -2442,8 +2476,8 @@ SparseMatrix* FiniteElementSpace::DerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
@@ -3161,7 +3195,7 @@ void FiniteElementSpace::CalcEdgeFaceVarOrders(
|
||||
else
|
||||
{
|
||||
// degenerate face (i.e., edge-face constraint)
|
||||
slave_orders |= edge_orders[-1 - slave.index];
|
||||
slave_orders |= edge_orders[FlipIndexSign(slave.index)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3969,11 +4003,8 @@ void FiniteElementSpace::Destroy()
|
||||
delete E2Q_array[i];
|
||||
}
|
||||
E2Q_array.SetSize(0);
|
||||
for (auto &x : L2F)
|
||||
{
|
||||
delete x.second;
|
||||
}
|
||||
L2F.clear();
|
||||
interpolations.clear();
|
||||
for (int i = 0; i < E2IFQ_array.Size(); i++)
|
||||
{
|
||||
delete E2IFQ_array[i];
|
||||
|
||||
+10
-13
@@ -13,6 +13,7 @@
|
||||
#define MFEM_FESPACE
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
#include "../linalg/ordering.hpp"
|
||||
#include "../linalg/sparsemat.hpp"
|
||||
#include "../mesh/mesh.hpp"
|
||||
@@ -320,18 +321,11 @@ protected:
|
||||
mutable OperatorHandle L2E_nat, L2E_lex;
|
||||
/// The face restriction operators, see GetFaceRestriction().
|
||||
using key_face = std::tuple<bool, ElementDofOrdering, FaceType, L2FaceValues>;
|
||||
struct key_hash
|
||||
{
|
||||
std::size_t operator()(const key_face& k) const
|
||||
{
|
||||
return std::get<0>(k)
|
||||
+ 2 * (int)std::get<1>(k)
|
||||
+ 4 * (int)std::get<2>(k)
|
||||
+ 8 * (int)std::get<3>(k);
|
||||
}
|
||||
};
|
||||
using map_L2F = std::unordered_map<const key_face,FaceRestriction*,key_hash>;
|
||||
mutable map_L2F L2F;
|
||||
mutable std::unordered_map<key_face,std::unique_ptr<FaceRestriction>,
|
||||
TupleHasher> L2F;
|
||||
|
||||
mutable std::unordered_map<std::tuple<ElementDofOrdering,FaceType>,
|
||||
std::unique_ptr<InterpolationManager>, TupleHasher> interpolations;
|
||||
|
||||
mutable Array<QuadratureInterpolator*> E2Q_array;
|
||||
mutable Array<FaceQuadratureInterpolator*> E2IFQ_array;
|
||||
@@ -751,6 +745,9 @@ public:
|
||||
ElementDofOrdering f_ordering, FaceType,
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const;
|
||||
|
||||
const InterpolationManager &GetInterpolationManager(
|
||||
ElementDofOrdering f_ordering, FaceType type) const;
|
||||
|
||||
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
|
||||
quadrature point values and/or derivatives (Q-vectors). */
|
||||
/** An E-vector represents the element-wise discontinuous version of the FE
|
||||
@@ -1153,7 +1150,7 @@ public:
|
||||
|
||||
/// Helper to return the DOF associated with a sign encoded DOF
|
||||
static inline int DecodeDof(int dof)
|
||||
{ return (dof >= 0) ? dof : (-1 - dof); }
|
||||
{ return UnsignIndex(dof); }
|
||||
|
||||
/// Helper to determine the DOF and sign of a sign encoded DOF
|
||||
static inline int DecodeDof(int dof, real_t& sign)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -5117,6 +5118,103 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
const PLBound &plb,
|
||||
const Vector &ref_range,
|
||||
const int vdim,
|
||||
Vector &lower, Vector &upper,
|
||||
Vector &control_pos) const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
const IntegrationRule ir_in = fe->GetNodes();
|
||||
IntegrationRule ir_new(ir_in.GetNPoints());
|
||||
const int dim = fes->GetMesh()->Dimension();
|
||||
const L2_FECollection *l2fec = dynamic_cast<const L2_FECollection *>
|
||||
(fes->FEColl());
|
||||
|
||||
const TensorBasisElement *tbe =
|
||||
dynamic_cast<const TensorBasisElement *>(fe);
|
||||
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
|
||||
|
||||
const Array<int> &dof_map = tbe->GetDofMap();
|
||||
bool lexico = (dof_map.Size() == 0);
|
||||
bool bern = (tbe->GetBasisType() == BasisType::Positive);
|
||||
bool h1 = (l2fec == nullptr);
|
||||
|
||||
Vector loc_data; // gridfunction values
|
||||
// Construct an integration rule to evaluate the gridfunction in
|
||||
// subinterval.
|
||||
for (int i = 0; i < ir_in.GetNPoints(); i++)
|
||||
{
|
||||
IntegrationPoint &ip_new = ir_new.IntPoint(i);
|
||||
const IntegrationPoint &ip_old =
|
||||
ir_in.IntPoint((lexico || bern) ? i : dof_map[i]);
|
||||
Vector ip_coord(dim);
|
||||
ip_old.Get(ip_coord.GetData(), dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
ip_coord(d) = ref_range(d) +
|
||||
(ref_range(dim+d) - ref_range(d)) * ip_coord(d);
|
||||
}
|
||||
ip_new.Set(ip_coord.GetData(), dim);
|
||||
}
|
||||
GetValues(elem, ir_new, loc_data, vdim);
|
||||
// At this point, the loc_data contains function values ordered
|
||||
// lexicographically, unless we are using Bernstein bases.
|
||||
// For Bernstein, we need to project and get coefficients first.
|
||||
|
||||
// For bernstein, we get coefficients corresponding to these function values
|
||||
if (bern)
|
||||
{
|
||||
int bt = 4; // BasisType::ClosedUniform
|
||||
int o = fe->GetOrder();
|
||||
DenseMatrix projmat;
|
||||
NodalTensorFiniteElement *ntfe = nullptr;
|
||||
if (dim == 1)
|
||||
{
|
||||
if (h1) { ntfe = new H1_SegmentElement(o, bt); }
|
||||
else { ntfe = new L2_SegmentElement(o, bt); }
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
if (h1) { ntfe = new H1_QuadrilateralElement(o, bt); }
|
||||
else { ntfe = new L2_QuadrilateralElement(o, bt); }
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
if (h1) { ntfe = new H1_HexahedronElement(o, bt); }
|
||||
else { ntfe = new L2_HexahedronElement(o, bt); }
|
||||
}
|
||||
// projection matrix from H1 to Positive
|
||||
ElementTransformation *eltran = fes->GetElementTransformation(elem);
|
||||
fe->Project(*ntfe, *eltran, projmat);
|
||||
Vector loc_data_temp(loc_data.Size());
|
||||
projmat.Mult(loc_data, loc_data_temp);
|
||||
for (int i = 0; i < dof_map.Size(); i++)
|
||||
{
|
||||
loc_data(i) = loc_data_temp(dof_map[i]);
|
||||
}
|
||||
if (dof_map.Size() == 0) { loc_data = loc_data_temp; }
|
||||
delete ntfe;
|
||||
}
|
||||
|
||||
// Get bounds at control points
|
||||
plb.GetNDBounds(dim, loc_data, lower, upper);
|
||||
|
||||
// Save control point positions
|
||||
int ncp = plb.GetNControlPoints();
|
||||
control_pos.SetSize(dim * ncp);
|
||||
const Vector control_pos_1D = plb.GetControlPoints();
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
control_pos(i + d*ncp) =
|
||||
ref_range(d) + (ref_range(dim+d)-ref_range(d))*control_pos_1D(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim) const
|
||||
@@ -5197,6 +5295,467 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
return plb;
|
||||
}
|
||||
|
||||
struct IntervalNode
|
||||
{
|
||||
real_t val_min;
|
||||
real_t val_max;
|
||||
Array<IntervalNode *> child;
|
||||
IntervalNode(real_t vmin, real_t vmax)
|
||||
: val_min(vmin), val_max(vmax)
|
||||
{
|
||||
child.SetSize(0);
|
||||
}
|
||||
void AddChild(IntervalNode *ch) { child.Append(ch); }
|
||||
real_t GetChildMinLower()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_min;
|
||||
}
|
||||
real_t valmin = numeric_limits<real_t>::max();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMinLower();
|
||||
valmin = std::min(valmin, candidate);
|
||||
}
|
||||
return valmin;
|
||||
}
|
||||
real_t GetChildMinUpper()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_max;
|
||||
}
|
||||
real_t valmax = numeric_limits<real_t>::max();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMinUpper();
|
||||
valmax = std::min(valmax, candidate);
|
||||
}
|
||||
return valmax;
|
||||
}
|
||||
real_t GetChildMaxLower()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_min;
|
||||
}
|
||||
real_t valmin = numeric_limits<real_t>::lowest();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMaxLower();
|
||||
valmin = std::max(valmin, candidate);
|
||||
}
|
||||
return valmin;
|
||||
}
|
||||
real_t GetChildMaxUpper()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_max;
|
||||
}
|
||||
real_t valmax = numeric_limits<real_t>::lowest();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMaxUpper();
|
||||
valmax = std::max(valmax, candidate);
|
||||
}
|
||||
return valmax;
|
||||
}
|
||||
void DeleteChildren()
|
||||
{
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
child[i]->DeleteChildren();
|
||||
delete child[i];
|
||||
}
|
||||
child.SetSize(0);
|
||||
}
|
||||
};
|
||||
|
||||
struct SearchInterval
|
||||
{
|
||||
Vector ref_range;
|
||||
int depth;
|
||||
IntervalNode *node;
|
||||
SearchInterval(const Vector &ref_range_in, int d, IntervalNode *n)
|
||||
: ref_range(ref_range_in), depth(d), node(n)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct IntervalCompareMin
|
||||
{
|
||||
bool operator()(const SearchInterval *a, const SearchInterval *b) const
|
||||
{
|
||||
return a->node->val_min > b->node->val_min;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntervalCompareMax
|
||||
{
|
||||
bool operator()(const SearchInterval *a, const SearchInterval *b) const
|
||||
{
|
||||
return a->node->val_max < b->node->val_max;
|
||||
}
|
||||
};
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol) const
|
||||
{
|
||||
real_t min_threshold = std::numeric_limits<real_t>::max();
|
||||
return EstimateFunctionMinimum(elem, plb, vdim, max_depth, tol,
|
||||
min_threshold);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol, real_t &min_threshold) const
|
||||
{
|
||||
const int dim = this->FESpace()->GetMesh()->Dimension();
|
||||
const int ncp = plb.GetNControlPoints();
|
||||
Vector pos_range(2*dim); pos_range = 0.0;
|
||||
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
|
||||
Vector lower, upper, cp_ref_loc;
|
||||
|
||||
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
|
||||
real_t val_min = lower.Min();
|
||||
real_t val_max = upper.Min();
|
||||
|
||||
min_threshold = std::min(min_threshold, val_max);
|
||||
|
||||
// Pruning: if the element's lower bound is greater than the current global
|
||||
// upper bound, this element cannot contain the global minimum.
|
||||
if (val_min >= min_threshold)
|
||||
{
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
|
||||
if (val_min == val_max || max_depth == 0)
|
||||
{
|
||||
min_threshold = std::min(min_threshold, val_min);
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
real_t abs_tol = tol*(val_max-val_min);
|
||||
|
||||
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
|
||||
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
|
||||
initial_node);
|
||||
|
||||
std::priority_queue<SearchInterval*,
|
||||
std::vector<SearchInterval*>, IntervalCompareMin> pq;
|
||||
pq.push(initial_interval);
|
||||
|
||||
real_t min_upper_bound = upper.Min();
|
||||
real_t min_lower_bound = lower.Min();
|
||||
|
||||
while (!pq.empty())
|
||||
{
|
||||
SearchInterval *current = pq.top();
|
||||
pq.pop();
|
||||
int curr_depth = current->depth;
|
||||
|
||||
// Reached max depth or this interval cannot contain the global minimum
|
||||
if (current->node->val_min >= min_threshold || curr_depth >= max_depth)
|
||||
{
|
||||
delete current;
|
||||
continue;
|
||||
}
|
||||
|
||||
min_lower_bound = initial_node->GetChildMinLower();
|
||||
if (min_upper_bound - min_lower_bound < abs_tol)
|
||||
{
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subdivide the interval and get bounds on it
|
||||
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
|
||||
vdim, lower, upper, cp_ref_loc);
|
||||
|
||||
// process the bounds and create sub-intervals
|
||||
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
|
||||
{
|
||||
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
|
||||
{
|
||||
for (int i = 0; i < ncp-1; i++)
|
||||
{
|
||||
real_t lv = 0.0, uv = 0.0;
|
||||
if (dim == 1)
|
||||
{
|
||||
lv = std::min(lower(i), lower(i+1));
|
||||
uv = std::min(upper(i), upper(i+1));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
lv = std::min({lower(i + j*ncp), lower((i+1) + j*ncp),
|
||||
lower(i + (j+1)*ncp),
|
||||
lower((i+1) + (j+1)*ncp)});
|
||||
uv = std::min({upper(i + j*ncp), upper((i+1) + j*ncp),
|
||||
upper(i + (j+1)*ncp),
|
||||
upper((i+1) + (j+1)*ncp)});
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
lv = std::min({lower(i + j*ncp + k*ncp*ncp),
|
||||
lower((i+1) + j*ncp + k*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + k*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
lower(i + j*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
uv = std::min({upper(i + j*ncp + k*ncp*ncp),
|
||||
upper((i+1) + j*ncp + k*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + k*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
upper(i + j*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
}
|
||||
IntervalNode *child_node = new IntervalNode(lv, uv);
|
||||
current->node->AddChild(child_node);
|
||||
|
||||
if (lv < min_threshold)
|
||||
{
|
||||
min_upper_bound = std::min(min_upper_bound, uv);
|
||||
min_threshold = std::min(min_threshold, uv);
|
||||
if (curr_depth < max_depth)
|
||||
{
|
||||
pos_range(0) = cp_ref_loc(i);
|
||||
pos_range(0+dim) = cp_ref_loc(i+1);
|
||||
if (dim >= 2)
|
||||
{
|
||||
pos_range(1) = cp_ref_loc(ncp + j);
|
||||
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
pos_range(2) = cp_ref_loc(2*ncp + k);
|
||||
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
|
||||
}
|
||||
SearchInterval *child_interval =
|
||||
new SearchInterval(pos_range, curr_depth + 1,
|
||||
child_node);
|
||||
pq.push(child_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
|
||||
// clean up remaining intervals in queue
|
||||
while (!pq.empty())
|
||||
{
|
||||
delete pq.top();
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
min_lower_bound = initial_node->GetChildMinLower();
|
||||
initial_node->DeleteChildren();
|
||||
delete initial_node;
|
||||
|
||||
min_threshold = std::min(min_threshold, min_lower_bound);
|
||||
return std::make_pair(min_lower_bound, min_upper_bound);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol) const
|
||||
{
|
||||
real_t max_threshold = std::numeric_limits<real_t>::lowest();
|
||||
return EstimateFunctionMaximum(elem, plb, vdim, max_depth, tol,
|
||||
max_threshold);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol, real_t &max_threshold) const
|
||||
{
|
||||
const int dim = this->FESpace()->GetMesh()->Dimension();
|
||||
const int ncp = plb.GetNControlPoints();
|
||||
Vector pos_range(2*dim); pos_range = 0.0;
|
||||
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
|
||||
Vector lower, upper, cp_ref_loc;
|
||||
|
||||
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
|
||||
real_t val_min = lower.Max();
|
||||
real_t val_max = upper.Max();
|
||||
|
||||
max_threshold = std::max(max_threshold, val_min);
|
||||
|
||||
// Pruning: if the element's upper bound is less than the current global
|
||||
// lower bound, this element cannot contain the global maximum.
|
||||
if (val_max <= max_threshold)
|
||||
{
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
|
||||
if (val_min == val_max || max_depth == 0)
|
||||
{
|
||||
max_threshold = std::max(max_threshold, val_max);
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
real_t abs_tol = tol*(val_max-val_min);
|
||||
|
||||
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
|
||||
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
|
||||
initial_node);
|
||||
|
||||
std::priority_queue<SearchInterval*,
|
||||
std::vector<SearchInterval*>, IntervalCompareMax> pq;
|
||||
pq.push(initial_interval);
|
||||
|
||||
real_t max_lower_bound = val_min;
|
||||
real_t max_upper_bound = val_max;
|
||||
|
||||
while (!pq.empty())
|
||||
{
|
||||
SearchInterval *current = pq.top();
|
||||
pq.pop();
|
||||
int curr_depth = current->depth;
|
||||
|
||||
// Reached max depth or this interval cannot contain the global maximum.
|
||||
if (current->node->val_max <= max_threshold || curr_depth >= max_depth)
|
||||
{
|
||||
delete current;
|
||||
continue;
|
||||
}
|
||||
|
||||
max_upper_bound = initial_node->GetChildMaxUpper();
|
||||
if (max_upper_bound - max_lower_bound < abs_tol)
|
||||
{
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subdivide the interval and get bounds on it
|
||||
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
|
||||
vdim, lower, upper, cp_ref_loc);
|
||||
|
||||
// process the bounds and create sub-intervals
|
||||
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
|
||||
{
|
||||
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
|
||||
{
|
||||
for (int i = 0; i < ncp-1; i++)
|
||||
{
|
||||
real_t lv = 0.0, uv = 0.0;
|
||||
if (dim == 1)
|
||||
{
|
||||
lv = std::max(lower(i), lower(i+1));
|
||||
uv = std::max(upper(i), upper(i+1));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
lv = std::max({lower(i + j*ncp), lower((i+1) + j*ncp),
|
||||
lower(i + (j+1)*ncp),
|
||||
lower((i+1) + (j+1)*ncp)});
|
||||
uv = std::max({upper(i + j*ncp), upper((i+1) + j*ncp),
|
||||
upper(i + (j+1)*ncp),
|
||||
upper((i+1) + (j+1)*ncp)});
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
lv = std::max({lower(i + j*ncp + k*ncp*ncp),
|
||||
lower((i+1) + j*ncp + k*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + k*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
lower(i + j*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
uv = std::max({upper(i + j*ncp + k*ncp*ncp),
|
||||
upper((i+1) + j*ncp + k*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + k*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
upper(i + j*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
}
|
||||
IntervalNode *child_node = new IntervalNode(lv, uv);
|
||||
current->node->AddChild(child_node);
|
||||
|
||||
if (uv > max_threshold)
|
||||
{
|
||||
max_lower_bound = std::max(max_lower_bound, lv);
|
||||
max_threshold = std::max(max_threshold, lv);
|
||||
if (curr_depth < max_depth)
|
||||
{
|
||||
pos_range(0) = cp_ref_loc(i);
|
||||
pos_range(0+dim) = cp_ref_loc(i+1);
|
||||
if (dim >= 2)
|
||||
{
|
||||
pos_range(1) = cp_ref_loc(ncp + j);
|
||||
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
pos_range(2) = cp_ref_loc(2*ncp + k);
|
||||
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
|
||||
}
|
||||
SearchInterval *child_interval =
|
||||
new SearchInterval(pos_range, curr_depth + 1,
|
||||
child_node);
|
||||
pq.push(child_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
// clean up remaining intervals in queue
|
||||
while (!pq.empty())
|
||||
{
|
||||
delete pq.top();
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
max_upper_bound = initial_node->GetChildMaxUpper();
|
||||
initial_node->DeleteChildren();
|
||||
delete initial_node;
|
||||
max_threshold = std::max(max_threshold, max_upper_bound);
|
||||
|
||||
return std::make_pair(max_lower_bound, max_upper_bound);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
real_t global_min_lower = std::numeric_limits<real_t>::max();
|
||||
real_t global_min_upper = std::numeric_limits<real_t>::max();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
std::pair<real_t, real_t> min_pair =
|
||||
EstimateFunctionMinimum(i, plb, vdim, max_depth, tol,
|
||||
global_min_lower);
|
||||
global_min_upper = std::min(global_min_upper, min_pair.second);
|
||||
}
|
||||
return std::make_pair(global_min_lower, global_min_upper);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
real_t global_max_lower = std::numeric_limits<real_t>::lowest();
|
||||
real_t global_max_upper = std::numeric_limits<real_t>::lowest();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
std::pair<real_t, real_t> max_pair =
|
||||
EstimateFunctionMaximum(i, plb, vdim, max_depth, tol,
|
||||
global_max_upper);
|
||||
global_max_lower = std::max(global_max_lower, max_pair.first);
|
||||
}
|
||||
return std::make_pair(global_max_lower, global_max_upper);
|
||||
}
|
||||
|
||||
}
|
||||
+117
-7
@@ -564,6 +564,70 @@ protected:
|
||||
/// P-refinement version of Update().
|
||||
void UpdatePRef();
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem
|
||||
* if it is below a certain @a min_threshold.
|
||||
*
|
||||
* @details For a given element \p elem and grid function component \p vdim
|
||||
* an estimate of the function minimum is the minimum of the piecewise
|
||||
* linear lower bound obtained using the given PLBound object. The actual
|
||||
* minimum is between [minimum lower bound, minimum upper bound]. We
|
||||
* improve the estimate of the function minimum by recursively
|
||||
* subdividing the interval with the lowest lower bound, and computing
|
||||
* bounds on the sub-intervals.
|
||||
* This process continues until (i) the maximum recursion depth is reached
|
||||
* or (ii) the difference between the minimum upper bound and minimum lower
|
||||
* bound is less than a certain tolerance (\p tol * [initial maximum
|
||||
* upper bound - initial minimum lower bound]).
|
||||
* The function also terminates if the lowest minima estimate is found
|
||||
* to be above the given threshold \p min_threshold. This is useful when
|
||||
* we are interested in computing the global minimum of the function
|
||||
* over all elements. In this case we can reject elements where the lowest
|
||||
* bound is above the current global minimum. In case the function
|
||||
* minimum on the element is below the global minimum, we update
|
||||
* \p min_threshold.
|
||||
*
|
||||
* We return a pair of values that bracket the actual minimum, i.e.
|
||||
* [min_lower_bound, min_upper_bound].
|
||||
*/
|
||||
std::pair<real_t,real_t> EstimateFunctionMinimum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol,
|
||||
real_t &min_threshold)const;
|
||||
|
||||
/** @brief Estimate the maximum value of the GridFunction in element @a elem
|
||||
* if it is below a certain @a max_threshold.
|
||||
*
|
||||
* @details For a given element \p elem and grid function component \p vdim
|
||||
* an estimate of the function maximum is the maximum of the piecewise
|
||||
* linear upper bound obtained using the given PLBound object. The actual
|
||||
* maximum is between [maximum lower bound, maximum upper bound]. We
|
||||
* improve the estimate of the function maximum by recursively
|
||||
* subdividing the interval with the highest upper bound, and computing
|
||||
* bounds on the sub-intervals.
|
||||
* This process continues until (i) the maximum recursion depth is reached
|
||||
* or (ii) the difference between the maximum upper bound and maximum lower
|
||||
* bound is less than a certain tolerance (\p tol * [initial maximum
|
||||
* upper bound - initial maximum lower bound]).
|
||||
* The function also terminates if the highest maxima estimate is found
|
||||
* to be below the given threshold \p max_threshold. This is useful when
|
||||
* we are interested in computing the global maximum of the function
|
||||
* over all elements. In this case we can reject elements where the upper
|
||||
* bound is below the current global maximum. In case the function
|
||||
* maximum on the element is above the global maximum, we update
|
||||
* \p max_threshold.
|
||||
*
|
||||
* We return a pair of values that bracket the actual maximum, i.e.
|
||||
* [max_lower_bound, max_upper_bound].
|
||||
*/
|
||||
std::pair<real_t,real_t> EstimateFunctionMaximum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol,
|
||||
real_t &max_threshold)const;
|
||||
|
||||
public:
|
||||
/** @brief For each vdof, counts how many elements contain the vdof,
|
||||
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
|
||||
@@ -1662,21 +1726,21 @@ public:
|
||||
*/
|
||||
///@{
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in @b lower and @b upper. We also return the
|
||||
/// points based on \p ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in \p lower and \p upper. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// We compute the bounds for each vdim if \p vdim < 1.
|
||||
/// Note: For most cases, this method/interface will be sufficient.
|
||||
virtual PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
/// ordered byVDim:
|
||||
/// points based on \p ref_factor, and returns the bounds for each element
|
||||
/// ordered byNodes:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// We compute the bounds for each vdim if \p vdim < 1.
|
||||
PLBound GetElementBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
@@ -1687,6 +1751,18 @@ public:
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1) const;
|
||||
|
||||
/** @brief Gets the bounds on given reference range inside an element.
|
||||
*
|
||||
* @details @a ref_range is a vector of size 2*dim that specifies the
|
||||
* lower and upper limits in each dimension of the reference element.
|
||||
* For example, in 2D, ref_range = [rmin, smin, rmax, smax].
|
||||
*/
|
||||
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
|
||||
const Vector &ref_range,
|
||||
const int vdim,
|
||||
Vector &lower, Vector &upper,
|
||||
Vector &control_pos) const;
|
||||
|
||||
/// Compute bounds on the grid function for the given element.
|
||||
/// The bounds are stored in @b lower and @b upper.
|
||||
void GetElementBounds(const int elem, const PLBound &plb,
|
||||
@@ -1694,11 +1770,45 @@ public:
|
||||
const int vdim = -1) const;
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byVDim:
|
||||
/// are returned in @b lower and @b upper, ordered byNodes:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
const int vdim=-1) const;
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
|
||||
*
|
||||
* @details See the protected version of EstimateFunctionMinimum for
|
||||
* details.
|
||||
*/
|
||||
std::pair<real_t, real_t> EstimateFunctionMinimum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
|
||||
*
|
||||
* @details See the protected version of EstimateFunctionMaximum for
|
||||
* details.
|
||||
*/
|
||||
std::pair<real_t, real_t> EstimateFunctionMaximum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the GridFunction minimum across all elements. */
|
||||
virtual std::pair<real_t,real_t> EstimateFunctionMinimum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the GridFunction maximum across all elements. */
|
||||
virtual std::pair<real_t,real_t> EstimateFunctionMaximum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
///@}
|
||||
|
||||
/// Destroys grid function.
|
||||
|
||||
+59
-32
@@ -234,7 +234,7 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
int point_pos_ordering)
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
|
||||
bool dev_mode = (point_pos.UseDevice() && Device::IsEnabled());
|
||||
@@ -482,7 +482,7 @@ void FindPointsGSLIB::SetupDevice()
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering)
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
if (!DEV.setup_device)
|
||||
{
|
||||
@@ -505,13 +505,13 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem,
|
||||
gsl_ref, gsl_dist, points_cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem,
|
||||
gsl_ref, gsl_dist, points_cnt);
|
||||
}
|
||||
|
||||
// Sync from device to host
|
||||
@@ -1085,7 +1085,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
#else
|
||||
void FindPointsGSLIB::SetupDevice() {};
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering) {};
|
||||
const int point_pos_ordering) {};
|
||||
void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
@@ -1094,7 +1094,8 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
#endif
|
||||
|
||||
void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
|
||||
int point_pos_ordering, const double bb_t,
|
||||
const int point_pos_ordering,
|
||||
const double bb_t,
|
||||
const double newt_tol, const int npt_max)
|
||||
{
|
||||
if (!setupflag || (mesh != &m) )
|
||||
@@ -1105,16 +1106,28 @@ void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out, field_out_ordering);
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(Mesh &m, const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
FindPoints(m, point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
@@ -1470,7 +1483,7 @@ void FindPointsGSLIB::SetupSplitMeshesAndIntegrationRules(const int order)
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
|
||||
Vector &node_vals)
|
||||
Vector &node_vals) const
|
||||
{
|
||||
const GridFunction *nodes = gf_in;
|
||||
const FiniteElementSpace *fes = nodes->FESpace();
|
||||
@@ -1758,6 +1771,13 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
{
|
||||
Interpolate(field_in, field_out, field_in.FESpace()->GetOrdering());
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
const int gf_order = field_in.FESpace()->GetMaxElementOrder(),
|
||||
mesh_order = mesh->GetNodalFESpace()->GetMaxElementOrder();
|
||||
@@ -1800,7 +1820,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
const int maxOrder = field_in.FESpace()->GetMaxElementOrder();
|
||||
|
||||
InterpolateOnDevice(node_vals, field_out, NE_split_total, ncomp,
|
||||
maxOrder+1, field_in.FESpace()->GetOrdering());
|
||||
maxOrder+1, field_out_ordering);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
@@ -1812,12 +1832,13 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
field_in.FESpace()->IsVariableOrder() ==
|
||||
mesh->GetNodalFESpace()->IsVariableOrder())
|
||||
{
|
||||
InterpolateH1(field_in, field_out);
|
||||
InterpolateH1(field_in, field_out, field_out_ordering);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in, field_out);
|
||||
InterpolateGeneral(field_in, field_out,
|
||||
field_out_ordering);
|
||||
if (!fec_l2 || avgtype == AvgType::NONE) { return; }
|
||||
}
|
||||
|
||||
@@ -1861,11 +1882,11 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
|
||||
if (gf_order_h1 == mesh_order) // basis is GaussLobatto by default
|
||||
{
|
||||
InterpolateH1(field_in_h1, field_out_l2);
|
||||
InterpolateH1(field_in_h1, field_out_l2, field_out_ordering);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in_h1, field_out_l2);
|
||||
InterpolateGeneral(field_in_h1, field_out_l2, field_out_ordering);
|
||||
}
|
||||
|
||||
// Copy interpolated values for the points on element border
|
||||
@@ -1873,7 +1894,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
{
|
||||
for (int i = 0; i < indl2.Size(); i++)
|
||||
{
|
||||
int idx = field_in_h1.FESpace()->GetOrdering() == Ordering::byNODES?
|
||||
int idx = field_out_ordering == Ordering::byNODES?
|
||||
indl2[i] + j*points_cnt:
|
||||
indl2[i]*ncomp + j;
|
||||
field_out(idx) = field_out_l2(idx);
|
||||
@@ -1883,7 +1904,8 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
|
||||
if (field_in.FESpace()->IsVariableOrder())
|
||||
@@ -1913,7 +1935,8 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
dataptrout = i*points_cnt;
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin, points_fld);
|
||||
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin,
|
||||
points_fld);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1945,7 +1968,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
(gslib::findpts_data_3 *)this->fdataD);
|
||||
}
|
||||
}
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byVDIM)
|
||||
if (field_out_ordering == Ordering::byVDIM)
|
||||
{
|
||||
Vector field_out_temp = field_out;
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
@@ -1959,7 +1982,8 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
int ncomp = field_in.VectorDim(),
|
||||
nptorig = points_cnt,
|
||||
@@ -1979,7 +2003,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
if (dim == 3) { ip.z = gsl_mfem_ref(index*dim + 2); }
|
||||
Vector localval(ncomp);
|
||||
field_in.GetVectorValue(gsl_mfem_elem[index], ip, localval);
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
if (field_out_ordering == Ordering::byNODES)
|
||||
{
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
{
|
||||
@@ -2014,7 +2038,10 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
if (gsl_code[index] == 2) { continue; }
|
||||
for (int d = 0; d < dim; ++d) { pt->r[d]= gsl_mfem_ref(index*dim + d); }
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->r[d]= gsl_mfem_ref(index*dim + d);
|
||||
}
|
||||
pt->index = index;
|
||||
pt->proc = gsl_proc[index];
|
||||
pt->el = gsl_mfem_elem[index];
|
||||
@@ -2104,7 +2131,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
sdpt = (struct send_pt *)sendpt->ptr;
|
||||
for (int index = 0; index < static_cast<int>(sendpt->n); index++)
|
||||
{
|
||||
int idx = field_in.FESpace()->GetOrdering() == Ordering::byNODES ?
|
||||
int idx = field_out_ordering == Ordering::byNODES ?
|
||||
sdpt->index + j*nptorig :
|
||||
sdpt->index*ncomp + j;
|
||||
field_out(idx) = sdpt->ival;
|
||||
@@ -2246,7 +2273,7 @@ void FindPointsGSLIB::DistributeInterpolatedValues(const Vector &int_vals,
|
||||
}
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb)
|
||||
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb) const
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
|
||||
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
|
||||
@@ -2317,7 +2344,7 @@ void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb)
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV)
|
||||
Vector &obbV) const
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
|
||||
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
|
||||
@@ -2502,8 +2529,8 @@ void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
|
||||
}
|
||||
|
||||
void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
Array<unsigned int> &point_id,
|
||||
int point_pos_ordering)
|
||||
const Array<unsigned int> &point_id,
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Use OversetFindPointsGSLIB::Setup before "
|
||||
"finding points.");
|
||||
@@ -2582,10 +2609,10 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
}
|
||||
|
||||
void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
Array<unsigned int> &point_id,
|
||||
const Array<unsigned int> &point_id,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_id, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
|
||||
+32
-15
@@ -119,11 +119,13 @@ protected:
|
||||
} DEV;
|
||||
|
||||
/// Use GSLIB for communication and interpolation
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out);
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/// Uses GSLIB Crystal Router for communication followed by MFEM's
|
||||
/// interpolation functions
|
||||
virtual void InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out);
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/// Since GSLIB is designed to work with quads/hexes, we split every
|
||||
/// triangle/tet/prism/pyramid element into quads/hexes.
|
||||
@@ -140,7 +142,7 @@ protected:
|
||||
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
|
||||
|
||||
/// Get GridFunction value at the points expected by GSLIB.
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals);
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
|
||||
|
||||
/// Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices,
|
||||
/// find the original element number (that was split into micro quads/hexes)
|
||||
@@ -182,7 +184,7 @@ protected:
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by @a point_pos_ordering. */
|
||||
void FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
@@ -253,10 +255,15 @@ public:
|
||||
#gsl_dist Distance between the sought and the found point
|
||||
in physical space. */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPoints(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPoints(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES,
|
||||
const int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
@@ -266,20 +273,28 @@ public:
|
||||
\p field_in is in H1 and in the same space as the
|
||||
mesh that was given to Setup().
|
||||
@param[out] field_out Interpolated values. For points that are not found
|
||||
the value is set to #default_interp_value. */
|
||||
the value is set to #default_interp_value.
|
||||
The output ordering is determined from field_in.*/
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
|
||||
/// Interpolation of field values, with output ordering specification.
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/** Search positions and interpolate. The ordering (byNODES or byVDIM) of
|
||||
the output values in \p field_out corresponds to the ordering used
|
||||
in the input GridFunction \p field_in. */
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Search positions and interpolate with given point and output ordering.
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out, const int point_pos_ordering,
|
||||
const int field_out_ordering);
|
||||
/** Setup FindPoints, search positions and interpolate. The ordering (byNODES
|
||||
or byVDIM) of the output values in \p field_out corresponds to the
|
||||
ordering used in the input GridFunction \p field_in. */
|
||||
void Interpolate(Mesh &m, const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Average type to be used for L2 functions in-case a point is located at
|
||||
/// an element boundary where the function might be multi-valued.
|
||||
@@ -376,7 +391,7 @@ public:
|
||||
/// The size of the returned vector is (nel x nverts x dim), where nel is the
|
||||
/// number of elements (after splitting for simplcies), nverts is number of
|
||||
/// vertices (4 in 2D, 8 in 3D), and dim is the spatial dimension.
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb);
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb) const;
|
||||
|
||||
/// Return the oriented bounding boxes (OBB) computed during \ref Setup.
|
||||
/// Each OBB is represented using the inverse transformation (A^{-1}) and
|
||||
@@ -386,7 +401,8 @@ public:
|
||||
/// size (dim x dim x nel), and the OBB centers are returned in \p obbC,
|
||||
/// a vector of size (nel x dim). The vertices of the OBBs are returned in
|
||||
/// \p obbV, a vector of size (nel x nverts x dim) .
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC, Vector &obbV);
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV) const;
|
||||
};
|
||||
|
||||
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
|
||||
@@ -446,13 +462,14 @@ public:
|
||||
byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
Array<unsigned int> &point_id,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const Array<unsigned int> &point_id,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Search positions and interpolate */
|
||||
void Interpolate(const Vector &point_pos, Array<unsigned int> &point_id,
|
||||
void Interpolate(const Vector &point_pos,
|
||||
const Array<unsigned int> &point_id,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
using FindPointsGSLIB::Interpolate;
|
||||
};
|
||||
|
||||
|
||||
@@ -789,7 +789,6 @@ void Hybridization::ComputeH()
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add ones on the diagonal of zero rows
|
||||
V->Finalize();
|
||||
Array<HYPRE_BigInt> V_J(V->NumNonZeroElems());
|
||||
MFEM_ASSERT(c_pfes, "");
|
||||
@@ -823,6 +822,13 @@ void Hybridization::ComputeH()
|
||||
MFEM_VERIFY(pH.Type() != Operator::PETSC_MATIS, "To be implemented");
|
||||
pH.MakePtAP(plpH, pP);
|
||||
delete lpH;
|
||||
|
||||
HypreParMatrix *hH = pH.As<HypreParMatrix>();
|
||||
MFEM_ASSERT(hH, "");
|
||||
|
||||
SparseMatrix H_diag;
|
||||
hH->GetDiag(H_diag);
|
||||
H_diag.SetDiagIdentity();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+455
-275
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,11 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/array.hpp"
|
||||
#include "../linalg/operator.hpp"
|
||||
#include "../linalg/vector.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -45,15 +48,30 @@ protected:
|
||||
Array<int> hat_dof_gather_map;
|
||||
Array<DofType> hat_dof_marker;
|
||||
|
||||
Array<int> el_to_face;
|
||||
Array<int> face_to_el;
|
||||
Array<int> el_to_face; ///< Element to face connectivity.
|
||||
Array<int> el_face_offsets; ///< Per-element offsets into @a el_to_face.
|
||||
Array<int> face_to_el; ///< Face-to-element connectivity.
|
||||
Array<int> face_face_offsets; ///< Face-to-face offsets.
|
||||
|
||||
int n_el_face; ///< Total number of element-to-face connections.
|
||||
int n_face_face; ///< Total number of face-to-face connections.
|
||||
|
||||
Vector Ct_mat; ///< Constraint matrix (transposed) stored element-wise.
|
||||
|
||||
/// @name For parallel non-conforming meshes
|
||||
///@{
|
||||
std::unique_ptr<Operator> P_pc; ///< Partially conforming prolongation.
|
||||
std::unique_ptr<Operator> P_nbr; ///< Face-neighbor prolongation.
|
||||
///@}
|
||||
|
||||
Array<int> idofs, bdofs;
|
||||
|
||||
Vector Ahat, Ahat_ii, Ahat_ib, Ahat_bi, Ahat_bb;
|
||||
Array<int> Ahat_ii_piv, Ahat_bb_piv;
|
||||
|
||||
/// Return the (partially) conforming prolongation on the constraint space.
|
||||
const Operator &GetProlongation() const;
|
||||
|
||||
public:
|
||||
/// Construct the constraint matrix.
|
||||
void ConstructC();
|
||||
|
||||
@@ -1004,13 +1004,16 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
|
||||
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(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
const auto b = Reshape(b_.Read(), Q1D, D1D);
|
||||
const auto g = Reshape(g_.Read(), Q1D, D1D);
|
||||
const auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
const auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
|
||||
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
|
||||
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(NE,
|
||||
Q1D, Q1D, Q1D,
|
||||
[=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
@@ -1133,11 +1133,11 @@ inline void SmemPAMassApply3D(const int NE,
|
||||
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
|
||||
MFEM_VERIFY(D1D <= max_d1d, "");
|
||||
MFEM_VERIFY(Q1D <= max_q1d, "");
|
||||
auto b = b_.Read();
|
||||
auto d = d_.Read();
|
||||
auto x = x_.Read();
|
||||
const auto b = b_.Read();
|
||||
const auto d = d_.Read();
|
||||
const auto x = x_.Read();
|
||||
auto y = y_.ReadWrite();
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
|
||||
});
|
||||
@@ -1156,8 +1156,8 @@ inline void EAMassAssemble1D(const int NE,
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
auto B = Reshape(basis.Read(), Q1D, D1D);
|
||||
auto D = Reshape(padata.Read(), Q1D, NE);
|
||||
const auto B = Reshape(basis.Read(), Q1D, D1D);
|
||||
const auto D = Reshape(padata.Read(), Q1D, NE);
|
||||
auto M = Reshape(add ? eadata.ReadWrite() : eadata.Write(), D1D, D1D, NE);
|
||||
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
const FaceType ftype = FaceType::Interior;
|
||||
const int nf = mesh.GetNFbyType(ftype);
|
||||
|
||||
const Geometry::Type geom = mesh.GetFaceGeometry(0);
|
||||
const Geometry::Type geom = mesh.GetTypicalFaceGeometry();
|
||||
const int trial_order = trial_fes.GetMaxElementOrder();
|
||||
const int test_order = test_fes.GetMaxElementOrder();
|
||||
const int qorder = test_order + trial_order - 1;
|
||||
@@ -47,7 +47,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
});
|
||||
}
|
||||
|
||||
const FiniteElement &trial_face_el = *trial_fes.GetFaceElement(0);
|
||||
const FiniteElement &trial_face_el = *trial_fes.GetTypicalTraceElement();
|
||||
const auto maps = &trial_face_el.GetDofToQuad(ir, DofToQuad::TENSOR);
|
||||
const int ndof_face = trial_face_el.GetDof();
|
||||
|
||||
@@ -72,7 +72,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
|
||||
const FiniteElement &test_el = *test_fes.GetFE(0);
|
||||
const FiniteElement &test_el = *test_fes.GetTypicalFE();
|
||||
const int n_faces_per_el = 2*dim; // assuming tensor product
|
||||
// Get all the local face maps (mapping from lexicographic face index to
|
||||
// lexicographic volume index, depending on the local face index).
|
||||
@@ -90,10 +90,10 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
Array<int> face_info(nf * 4);
|
||||
{
|
||||
int fidx = 0;
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
|
||||
if (!finfo.IsInterior()) { continue; }
|
||||
if (!finfo.IsInterior() || finfo.IsNonconformingCoarse()) { continue; }
|
||||
face_info[0 + fidx*4] = finfo.element[0].local_face_id;
|
||||
face_info[1 + fidx*4] = finfo.element[0].orientation;
|
||||
face_info[2 + fidx*4] = finfo.element[1].local_face_id;
|
||||
@@ -114,7 +114,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
else
|
||||
{
|
||||
d_emat = emat.Write();
|
||||
mfem::forall(emat.Size(), [=] MFEM_HOST_DEVICE (int i) { d_emat[i] = 0.0; });
|
||||
emat = 0.0; // Will execute on device, since Write() sets the device flag
|
||||
}
|
||||
|
||||
const auto face_mats = Reshape(mass_emat.Read(), ndof_face, ndof_face, nf);
|
||||
@@ -133,26 +133,104 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
}
|
||||
};
|
||||
|
||||
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
|
||||
auto permute_face_2 = [=] MFEM_HOST_DEVICE(int local_face_1, int local_face_2,
|
||||
int orient, int size1d, int index)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(el_i, z, 2)
|
||||
if (dim == 2)
|
||||
{
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
|
||||
return internal::PermuteFace2D(local_face_1, local_face_2, orient,
|
||||
size1d, index);
|
||||
}
|
||||
else // dim == 3
|
||||
{
|
||||
return internal::PermuteFace3D(local_face_1, local_face_2, orient,
|
||||
size1d, index);
|
||||
}
|
||||
};
|
||||
|
||||
if (mesh.Conforming())
|
||||
{
|
||||
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(el_i, z, 2)
|
||||
{
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
MFEM_FOREACH_THREAD(j, y, ndof_face)
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
|
||||
{
|
||||
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
MFEM_FOREACH_THREAD(j, y, ndof_face)
|
||||
{
|
||||
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const InterpolationManager &interp =
|
||||
test_fes.GetInterpolationManager(ElementDofOrdering::LEXICOGRAPHIC, ftype);
|
||||
|
||||
auto interp_configs = interp.GetFaceInterpConfig().Read();
|
||||
const int nc_size = interp.GetNumInterpolators();
|
||||
auto d_interp = Reshape(interp.GetInterpolators().Read(),
|
||||
ndof_face, ndof_face, nc_size);
|
||||
|
||||
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
const InterpConfig conf = interp_configs[f];
|
||||
const int master_side = conf.master_side;
|
||||
const int interp_index = conf.index;
|
||||
|
||||
const int lf_0 = d_face_info(0, 0, f);
|
||||
|
||||
for (int el_i = 0; el_i < 2; ++el_i)
|
||||
{
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
|
||||
for (int j = 0; j < ndof_face; j++)
|
||||
{
|
||||
for (int i_lex = 0; i_lex < ndof_face; i_lex++)
|
||||
{
|
||||
real_t val = 0.0;
|
||||
if (conf.is_non_conforming && el_i == master_side)
|
||||
{
|
||||
// Interpolate from el_i (coarse element) to the fine face.
|
||||
// The mapping is given by d_interp, which uses indices
|
||||
// relative to element 0.
|
||||
|
||||
// i0 is lexicographic relative to element 0
|
||||
const int i0 = permute_face_2(lf_i, lf_0, orient, d1d, i_lex);
|
||||
|
||||
// k0 is lexicographic relative to element 0
|
||||
for (int k0 = 0; k0 < ndof_face; k0++)
|
||||
{
|
||||
// k is relative to the face itself
|
||||
const int k = permute_face(lf_0, orient, d1d, k0);
|
||||
val += d_interp(k0, i0, interp_index)
|
||||
* face_mats(k, j, f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
val = face_mats(i_face, j, f);
|
||||
}
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
el_mats(i, j, el_i, f) += val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ void SmemPAVectorDiffusionApply2D(const int NE,
|
||||
const auto XE = Reshape(x.Read(), D1D, D1D, SDIM, NE);
|
||||
auto YE = Reshape(y.ReadWrite(), D1D, D1D, SDIM, NE);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
@@ -120,7 +120,7 @@ void SmemPAVectorDiffusionApply3D(const int NE,
|
||||
const auto XE = Reshape(x.Read(), D1D, D1D, D1D, SDIM, NE);
|
||||
auto YE = Reshape(y.ReadWrite(), D1D, D1D, D1D, SDIM, NE);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
@@ -171,15 +171,15 @@ template<int DIM, int T_SDIM, int T_D1D, int T_Q1D>
|
||||
VectorDiffusionIntegrator::ApplyKernelType
|
||||
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if (DIM == 2)
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
|
||||
}
|
||||
else if (DIM == 3)
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
|
||||
}
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
}
|
||||
|
||||
inline VectorDiffusionIntegrator::ApplyKernelType
|
||||
|
||||
@@ -51,7 +51,7 @@ void SmemPAVectorMassApply2D(const int NE,
|
||||
const auto X = Reshape(x.Read(), D1D, D1D, VDIM, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
@@ -119,7 +119,7 @@ void SmemPAVectorMassApply3D(const int NE,
|
||||
const auto X = Reshape(x.Read(), D1D, D1D, D1D, VDIM, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
@@ -182,15 +182,15 @@ template<int DIM, int T_D1D, int T_Q1D>
|
||||
VectorMassIntegrator::VectorMassAddMultPAType
|
||||
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
|
||||
{
|
||||
if (DIM == 2)
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
|
||||
}
|
||||
else if (DIM == 3)
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
}
|
||||
|
||||
inline VectorMassIntegrator::VectorMassAddMultPAType
|
||||
|
||||
@@ -301,18 +301,14 @@ template <int DIM, int T_D1D, int T_Q1D>
|
||||
DomainLFIntegrator::AssembleKernelType
|
||||
DomainLFIntegrator::AssembleKernels::Kernel()
|
||||
{
|
||||
switch (DIM)
|
||||
{
|
||||
case 1:
|
||||
return DLFEvalAssemble1D<T_D1D, T_Q1D>;
|
||||
case 2:
|
||||
return DLFEvalAssemble2D<T_D1D, T_Q1D>;
|
||||
case 3:
|
||||
return DLFEvalAssemble3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
if constexpr (DIM == 1) { return DLFEvalAssemble1D<T_D1D, T_Q1D>; }
|
||||
if constexpr (DIM == 2) { return DLFEvalAssemble2D<T_D1D, T_Q1D>; }
|
||||
if constexpr (DIM == 3) { return DLFEvalAssemble3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
#endif
|
||||
|
||||
#endif // MFEM_LININTEG_DOMAIN_KERNELS_HPP
|
||||
|
||||
+3
-31
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "kernel_reporter.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -86,35 +87,6 @@ namespace mfem
|
||||
} \
|
||||
}
|
||||
|
||||
/// @brief Hashes variadic packs for which each type contained in the variadic
|
||||
/// pack has a specialization of `std::hash` available.
|
||||
///
|
||||
/// For example, packs containing int, bool, enum values, etc.
|
||||
template<typename ...KernelParameters>
|
||||
struct KernelDispatchKeyHash
|
||||
{
|
||||
private:
|
||||
template<int N>
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const { return 0; }
|
||||
|
||||
// The hashing formula here is taken directly from the Boost library, with
|
||||
// the magic number 0x9e3779b9 chosen to minimize hashing collisions.
|
||||
template<std::size_t N, typename THead, typename... TTail>
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const
|
||||
{
|
||||
constexpr int Index = N - sizeof...(TTail) - 1;
|
||||
auto lhs_hash = std::hash<THead>()(std::get<Index>(value));
|
||||
auto rhs_hash = operator()<N, TTail...>(value);
|
||||
return lhs_hash^(rhs_hash + 0x9e3779b9 + (lhs_hash<<6) + (lhs_hash>>2));
|
||||
}
|
||||
public:
|
||||
/// Returns the hash of the given @a value.
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const
|
||||
{
|
||||
return operator()<sizeof...(KernelParameters),KernelParameters...>(value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal { template<typename... Types> struct KernelTypeList { }; }
|
||||
|
||||
template<typename... T> class KernelDispatchTable { };
|
||||
@@ -128,8 +100,8 @@ class KernelDispatchTable<Kernels,
|
||||
internal::KernelTypeList<Params...>,
|
||||
internal::KernelTypeList<OptParams...>>
|
||||
{
|
||||
using TableType = std::unordered_map<std::tuple<Params...>,
|
||||
Signature, KernelDispatchKeyHash<Params...>>;
|
||||
using TableType =
|
||||
std::unordered_map<std::tuple<Params...>, Signature, TupleHasher>;
|
||||
TableType table;
|
||||
|
||||
/// @brief Call function @a f with arguments @a args (perfect forwaring).
|
||||
|
||||
+6
-5
@@ -158,15 +158,16 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
|
||||
int i;
|
||||
i = dofmap_lor[off_lor + i1 + i2*2];
|
||||
int s1 = i < 0 ? -1 : 1;
|
||||
int idof_lor = vdof_lor[absdof(i)];
|
||||
int idof_lor = vdof_lor[UnsignIndex(i)];
|
||||
i = dofmap_ho[off_ho + i1*n1 + i2*n2];
|
||||
int s2 = i < 0 ? -1 : 1;
|
||||
int idof_ho = vdof_ho[absdof(i)];
|
||||
int idof_ho = vdof_ho[UnsignIndex(i)];
|
||||
int s3 = idof_lor < 0 ? -1 : 1;
|
||||
int s4 = idof_ho < 0 ? -1 : 1;
|
||||
int s = s1*s2*s3*s4;
|
||||
i = absdof(idof_ho);
|
||||
perm_[absdof(idof_lor)] = s < 0 ? -1-absdof(i) : absdof(i);
|
||||
i = UnsignIndex(idof_ho);
|
||||
perm_[UnsignIndex(idof_lor)] = s < 0 ? -1-UnsignIndex(i) :
|
||||
UnsignIndex(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -232,7 +233,7 @@ void LORBase::ConstructDofPermutation() const
|
||||
int j = l_perm[i];
|
||||
int s = j < 0 ? -1 : 1;
|
||||
int t_i = pfes_lor->GetLocalTDofNumber(i);
|
||||
int t_j = pfes_ho->GetLocalTDofNumber(absdof(j));
|
||||
int t_j = pfes_ho->GetLocalTDofNumber(UnsignIndex(j));
|
||||
// Either t_i and t_j both -1, or both non-negative
|
||||
if ((t_i < 0 && t_j >=0) || (t_j < 0 && t_i >= 0))
|
||||
{
|
||||
|
||||
@@ -57,8 +57,6 @@ private:
|
||||
/// values (after temporarily changing them for LOR assembly).
|
||||
void ResetIntegrationRules(GetIntegratorsFn get_integrators);
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
|
||||
protected:
|
||||
enum FESpaceType { H1, ND, RT, L2, INVALID };
|
||||
|
||||
|
||||
@@ -488,10 +488,16 @@ void ParBilinearForm::FormLinearSystem(
|
||||
R.Mult(x, true_X);
|
||||
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
ConstrainedOperator *A_constrained;
|
||||
Operator::FormConstrainedSystemOperator(ess_tdof_list, A_constrained);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
A_constrained->EliminateRHS(true_X, true_B);
|
||||
delete A_constrained;
|
||||
R.MultTranspose(true_B, b);
|
||||
hybridization->ReduceRHS(true_B, B);
|
||||
X.SetSize(B.Size());
|
||||
|
||||
+49
-56
@@ -424,7 +424,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -462,7 +462,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -500,7 +500,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -538,16 +538,16 @@ void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
|
||||
{
|
||||
if (dofs[i] < 0)
|
||||
{
|
||||
if (ldof_sign[-1-dofs[i]] < 0)
|
||||
if (ldof_sign[FlipIndexSign(dofs[i])] < 0)
|
||||
{
|
||||
dofs[i] = -1-dofs[i];
|
||||
dofs[i] = FlipIndexSign(dofs[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ldof_sign[dofs[i]] < 0)
|
||||
{
|
||||
dofs[i] = -1-dofs[i];
|
||||
dofs[i] = FlipIndexSign(dofs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -646,39 +646,38 @@ const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
|
||||
auto itr = L2F.find(key);
|
||||
if (itr != L2F.end())
|
||||
{
|
||||
return itr->second;
|
||||
return itr->second.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
FaceRestriction *res;
|
||||
std::unique_ptr<FaceRestriction> res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res = new ParL2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new ParL2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ParNCL2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new ParNCL2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
|
||||
{
|
||||
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res = new ConformingFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ParNCH1FaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new ParNCH1FaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
}
|
||||
L2F[key] = res;
|
||||
return res;
|
||||
return L2F.emplace(key, std::move(res)).first->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +699,8 @@ void ParFiniteElementSpace::GetSharedEdgeDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
dofs[i] = di >= 0 ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,7 +724,8 @@ void ParFiniteElementSpace::GetSharedTriangleDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
dofs[i] = di >= 0 ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,7 +749,8 @@ void ParFiniteElementSpace::GetSharedQuadrilateralDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1488,7 +1490,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
int ldof = UnsignIndex(ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
{
|
||||
@@ -1549,7 +1551,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
int ldof = UnsignIndex(ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
{
|
||||
@@ -1574,14 +1576,15 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
|
||||
for (int i = 0; i < num_ldofs; i++)
|
||||
{
|
||||
int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
|
||||
int ldof = UnsignIndex(ldofs_fn[i]);
|
||||
ldof_marker[ldof] = i;
|
||||
}
|
||||
|
||||
for ( ; j < j_end; j++)
|
||||
{
|
||||
int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
|
||||
const int ldof = UnsignIndex(send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] :
|
||||
FlipIndexSign(ldof_marker[ldof]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1673,12 +1676,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
{
|
||||
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
|
||||
{
|
||||
int ldof = face_nbr_ldof.GetJ()[j];
|
||||
if (ldof < 0)
|
||||
{
|
||||
ldof = -1-ldof;
|
||||
}
|
||||
|
||||
const int ldof = UnsignIndex(face_nbr_ldof.GetJ()[j]);
|
||||
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
|
||||
}
|
||||
}
|
||||
@@ -1722,7 +1720,7 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
MFEM_ASSERT(Nonconforming() && i >= pmesh->GetNumFaces(), "");
|
||||
int el1, el2, inf1, inf2;
|
||||
pmesh->GetFaceElements(i, &el1, &el2);
|
||||
el2 = -1 - el2;
|
||||
el2 = FlipIndexSign(el2);
|
||||
pmesh->GetFaceInfos(i, &inf1, &inf2);
|
||||
MFEM_ASSERT(0 <= el2 && el2 < face_nbr_element_dof.Size(), "");
|
||||
const int nd = face_nbr_element_dof.RowSize(el2);
|
||||
@@ -1738,7 +1736,8 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
{
|
||||
const int ldof = vdofs[j];
|
||||
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
|
||||
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] :
|
||||
FlipIndexSign(vol_vdofs[FlipIndexSign(ldof)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2062,8 +2061,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
|
||||
|
||||
for (int j = 0; j < ne; j++)
|
||||
{
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
|
||||
/* */ : (-1 - (first + (-1 - ind[j])));
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
|
||||
FlipIndexSign(first + FlipIndexSign(ind[j]));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2073,8 +2072,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
|
||||
const int *ind = fec->DofOrderForOrientation(Geometry::SEGMENT, Eo[i]);
|
||||
for (int j = 0; j < ne; j++)
|
||||
{
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
|
||||
/* */ : (-1 - (first + (-1 - ind[j])));
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
|
||||
FlipIndexSign(first + FlipIndexSign(ind[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2867,7 +2866,7 @@ void NeighborRowMessage::Encode(int rank)
|
||||
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
edof = FlipIndexSign(edof);
|
||||
s = -1;
|
||||
}
|
||||
|
||||
@@ -3068,10 +3067,10 @@ void NeighborRowMessage::Decode(int rank)
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
real_t s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
edof = UnsignIndex(edof);
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
edof = FlipIndexSign(edof);
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
@@ -3122,10 +3121,10 @@ void NeighborRowMessage::Decode(int rank)
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
edof = UnsignIndex(edof);
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
edof = FlipIndexSign(edof);
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
@@ -4406,12 +4405,9 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
{
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
int row = DofToVDof(dofs[j], vd);
|
||||
if (row < 0) { row = -1 - row; }
|
||||
|
||||
int col = DofToVDof(old_dofs[j], vd, old_ndofs);
|
||||
if (col < 0) { col = -1 - col; }
|
||||
|
||||
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
|
||||
const int col = UnsignIndex(DofToVDof(old_dofs[j], vd,
|
||||
old_ndofs));
|
||||
i_diag[row] = col;
|
||||
}
|
||||
}
|
||||
@@ -4436,9 +4432,7 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
{
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
int row = DofToVDof(dofs[j], vd);
|
||||
if (row < 0) { row = -1 - row; }
|
||||
|
||||
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
|
||||
if (i_diag[row] == i_diag[row+1]) // diag row empty?
|
||||
{
|
||||
i_offd[row] = old_dofs[j + vd * dofs.Size()];
|
||||
@@ -4547,9 +4541,9 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
|
||||
int fine_rank = old_ranks[k];
|
||||
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
const int fine_rank = old_ranks[k];
|
||||
const int coarse_rank = (emb.parent < 0) ? FlipIndexSign(emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
|
||||
if (coarse_rank != MyRank && fine_rank == MyRank)
|
||||
{
|
||||
@@ -4637,8 +4631,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
@@ -4687,8 +4681,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int m = UnsignIndex(DofToVDof(dofs[i], vd));
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
|
||||
@@ -483,6 +483,8 @@ public:
|
||||
const FiniteElement *GetFaceNbrFaceFE(int i) const;
|
||||
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() { return face_nbr_glob_dof_map; }
|
||||
const HYPRE_BigInt *GetFaceNbrGlobalDofMap() { return face_nbr_glob_dof_map; }
|
||||
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() const
|
||||
{ return face_nbr_glob_dof_map; }
|
||||
ElementTransformation *GetFaceNbrElementTransformation(int i) const
|
||||
{ return pmesh->GetFaceNbrElementTransformation(i); }
|
||||
|
||||
|
||||
@@ -1568,6 +1568,39 @@ PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
return plb;
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMinimum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
std::pair<real_t, real_t> minmax =
|
||||
GridFunction::EstimateFunctionMinimum(vdim, plb, max_depth, tol);
|
||||
|
||||
real_t glob_min_lower = minmax.first;
|
||||
real_t glob_min_upper = minmax.second;
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_min_lower, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_min_upper, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
|
||||
return std::make_pair(glob_min_lower, glob_min_upper);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMaximum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
std::pair<real_t, real_t> minmax =
|
||||
GridFunction::EstimateFunctionMaximum(vdim, plb, max_depth, tol);
|
||||
|
||||
real_t glob_max_lower = minmax.first;
|
||||
real_t glob_max_upper = minmax.second;
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_max_lower, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_max_upper, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
return std::make_pair(glob_max_lower, glob_max_upper);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
@@ -609,6 +609,18 @@ public:
|
||||
PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const override;
|
||||
|
||||
/** @brief Estimate the GridFunction minimum across all elements. */
|
||||
std::pair<real_t, real_t> EstimateFunctionMinimum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const override;
|
||||
|
||||
/** @brief Estimate the GridFunction maximum across all elements. */
|
||||
std::pair<real_t, real_t> EstimateFunctionMaximum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const override;
|
||||
|
||||
/** Save the local portion of the ParGridFunction. This differs from the
|
||||
serial GridFunction::Save in that it takes into account the signs of
|
||||
the local dofs. */
|
||||
|
||||
@@ -994,7 +994,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
if ( face.IsConforming() )
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1010,7 +1009,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
else // Non-conforming face
|
||||
{
|
||||
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1028,7 +1026,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
else if (type==FaceType::Boundary && face.IsBoundary())
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1046,10 +1043,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
gather_offsets[i] += gather_offsets[i - 1];
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into a contiguous memory structure.
|
||||
interpolations.LinearizeInterpolatorMapIntoVector();
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void ParNCL2FaceRestriction::ComputeGatherIndices()
|
||||
|
||||
@@ -326,9 +326,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillI(SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
@@ -364,9 +362,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillJAndData(const Vector &fea_data,
|
||||
SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
+1
-4
@@ -271,10 +271,7 @@ inline void QuadratureFunction::GetValues(
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
// Make the values matrix memory an alias of the quadrature function memory
|
||||
Memory<real_t> &values_mem = values.GetMemory();
|
||||
values_mem.Delete();
|
||||
values_mem.MakeAlias(GetMemory(), vdim*s_offset, vdim*sl_size);
|
||||
values.SetSize(vdim, sl_size);
|
||||
values.MakeRef(GetMemory(), vdim*s_offset, vdim, sl_size);
|
||||
}
|
||||
|
||||
inline void QuadratureFunction::GetValues(
|
||||
|
||||
+7
-1
@@ -50,7 +50,13 @@ QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Fallback(
|
||||
int DIM, int SDIM, int D1D, int Q1D)
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
if (DIM == 1)
|
||||
{
|
||||
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<0,0,2>; }
|
||||
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<0,0,3>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface; }
|
||||
else if (DIM == 3)
|
||||
|
||||
+54
-5
@@ -56,6 +56,50 @@ inline void Det1D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int T_SDIM = 3>
|
||||
inline void Det1DSurface(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b);
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, T_SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
for (int q = 0; q < Q1D; q++)
|
||||
{
|
||||
real_t grad[T_SDIM];
|
||||
for (int s = 0; s < T_SDIM; s++) { grad[s] = 0.0; }
|
||||
for (int d = 0; d < D1D; d++)
|
||||
{
|
||||
const real_t gval = G(q, d);
|
||||
for (int s = 0; s < T_SDIM; s++)
|
||||
{
|
||||
grad[s] += gval * X(d, s, e);
|
||||
}
|
||||
}
|
||||
real_t norm2 = 0.0;
|
||||
for (int s = 0; s < T_SDIM; s++)
|
||||
{
|
||||
norm2 += grad[s] * grad[s];
|
||||
}
|
||||
Y(q, e) = std::sqrt(norm2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void Det2D(const int NE,
|
||||
const real_t *b,
|
||||
@@ -290,11 +334,16 @@ template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1)
|
||||
{
|
||||
if constexpr (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if constexpr (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
|
||||
else if constexpr (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
|
||||
}
|
||||
else if constexpr (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -203,10 +203,10 @@ template<int DIM, QVectorLayout Q_LAYOUT,
|
||||
QuadratureInterpolator::TensorEvalKernelType
|
||||
QuadratureInterpolator::TensorEvalKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -453,8 +453,15 @@ QuadratureInterpolator::TensorEvalHDivKernels::Kernel()
|
||||
{
|
||||
using namespace internal::quadrature_interpolator;
|
||||
static_assert(DIM == 2 || DIM == 3, "only DIM=2 and DIM=3 are implemented!");
|
||||
if (DIM == 2) { return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>; }
|
||||
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
MFEM_ABORT("only DIM=2 and DIM=3 are implemented!");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -592,10 +592,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
QuadratureInterpolator::GradKernelType
|
||||
QuadratureInterpolator::GradKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
@@ -603,10 +603,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
QuadratureInterpolator::CollocatedGradKernelType
|
||||
QuadratureInterpolator::CollocatedGradKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -542,7 +542,8 @@ void QuadratureInterpolator::Mult(const Vector &e_vec,
|
||||
}
|
||||
|
||||
MFEM_ASSERT(!(eval_flags & DETERMINANTS) || dim == vdim ||
|
||||
(dim == 2 && vdim == 3), "Invalid dimensions for determinants.");
|
||||
(dim == 2 && vdim == 3) || (dim == 1 && vdim == 2) ||
|
||||
(dim == 1 && vdim == 3), "Invalid dimensions for determinants.");
|
||||
MFEM_ASSERT(fespace->GetMesh()->GetNumGeometries(
|
||||
fespace->GetMesh()->Dimension()) == 1,
|
||||
"mixed meshes are not supported");
|
||||
@@ -751,10 +752,10 @@ template <int DIM, int VDIM, int ND, int NQ>
|
||||
EvalKernel QuadratureInterpolator::EvalKernels::Kernel()
|
||||
{
|
||||
using namespace internal::quadrature_interpolator;
|
||||
if (DIM == 1) { return Eval1D; }
|
||||
else if (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
|
||||
else if (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return Eval1D; }
|
||||
else if constexpr (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
|
||||
else if constexpr (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
template <int DIM>
|
||||
|
||||
+122
-48
@@ -844,8 +844,6 @@ void ConformingFaceRestriction::ComputeGatherIndices(
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
|
||||
void ConformingFaceRestriction::SetFaceDofsScatterIndices(
|
||||
const Mesh::FaceInformation &face,
|
||||
const int face_index,
|
||||
@@ -868,9 +866,9 @@ void ConformingFaceRestriction::SetFaceDofsScatterIndices(
|
||||
{
|
||||
const int lex_volume_dof = face_map[face_dof];
|
||||
const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof]; // signed
|
||||
const int volume_dof = absdof(s_volume_dof);
|
||||
const int volume_dof = UnsignIndex(s_volume_dof);
|
||||
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
|
||||
const int global_dof = absdof(s_global_dof);
|
||||
const int global_dof = UnsignIndex(s_global_dof);
|
||||
const int restriction_dof = face_dofs*face_index + face_dof;
|
||||
scatter_indices[restriction_dof] = s_global_dof;
|
||||
++gather_offsets[global_dof + 1];
|
||||
@@ -897,10 +895,10 @@ void ConformingFaceRestriction::SetFaceDofsGatherIndices(
|
||||
{
|
||||
const int lex_volume_dof = face_map[face_dof];
|
||||
const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof];
|
||||
const int volume_dof = absdof(s_volume_dof);
|
||||
const int volume_dof = UnsignIndex(s_volume_dof);
|
||||
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
|
||||
const int sgn = (s_global_dof >= 0) ? 1 : -1;
|
||||
const int global_dof = absdof(s_global_dof);
|
||||
const int global_dof = UnsignIndex(s_global_dof);
|
||||
const int restriction_dof = face_dofs*face_index + face_dof;
|
||||
const int s_restriction_dof = (sgn >= 0) ? restriction_dof : -1 -
|
||||
restriction_dof;
|
||||
@@ -1506,12 +1504,12 @@ void L2FaceRestriction::EnsureNormalDerivativeRestriction() const
|
||||
}
|
||||
}
|
||||
|
||||
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes,
|
||||
ElementDofOrdering ordering,
|
||||
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes_,
|
||||
ElementDofOrdering ordering_,
|
||||
FaceType type)
|
||||
: fes(fes),
|
||||
ordering(ordering),
|
||||
interp_config( fes.GetNFbyType(type) ),
|
||||
: fes(fes_),
|
||||
ordering(ordering_),
|
||||
interp_config(fes.GetNFbyType(type)),
|
||||
nc_cpt(0)
|
||||
{ }
|
||||
|
||||
@@ -1536,7 +1534,8 @@ void InterpolationManager::RegisterFaceCoarseToFineInterpolation(
|
||||
face.element[0].local_face_id +
|
||||
6*face.element[1].local_face_id +
|
||||
36*face.element[1].orientation ;
|
||||
// Unfortunately we can't trust unicity of the ptMat to identify the transformation.
|
||||
// Unfortunately we can't trust uniqueness of the ptMat to identify the
|
||||
// transformation.
|
||||
Key key(ptMat, face_key);
|
||||
auto itr = interp_map.find(key);
|
||||
if ( itr == interp_map.end() )
|
||||
@@ -1583,17 +1582,27 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
|
||||
IsoparametricTransformation isotr;
|
||||
isotr.SetIdentityTransformation(trace_fe->GetGeomType());
|
||||
isotr.SetPointMat(*ptMat);
|
||||
DenseMatrix& trans_pt_mat = isotr.GetPointMat();
|
||||
// PointMatrix needs to be flipped in 2D
|
||||
if ( trace_fe->GetGeomType()==Geometry::SEGMENT && !is_ghost_slave )
|
||||
{
|
||||
std::swap(trans_pt_mat(0,0),trans_pt_mat(0,1));
|
||||
}
|
||||
DenseMatrix native_interpolator(face_dofs,face_dofs);
|
||||
trace_fe->GetLocalInterpolation(isotr, native_interpolator);
|
||||
|
||||
if (trace_fe->GetMapType() == FiniteElement::INTEGRAL)
|
||||
{
|
||||
// Handle potentially inverted Jacobian matrix
|
||||
isotr.SetIntPoint(&Geometries.GetCenter(trace_fe->GetGeomType()));
|
||||
native_interpolator *= (isotr.Weight() >= 0) ? 1.0 : -1.0;
|
||||
}
|
||||
|
||||
const int dim = trace_fe->GetDim()+1;
|
||||
const int dof1d = trace_fe->GetOrder()+1;
|
||||
const int orientation = face.element[1].orientation;
|
||||
int orientation_i = face.element[1].orientation;
|
||||
const int orientation_j = face.element[1].orientation;
|
||||
|
||||
// In 2D, need to flip orientation of the segments`
|
||||
if (trace_fe->GetGeomType() == Geometry::SEGMENT && !is_ghost_slave)
|
||||
{
|
||||
orientation_i = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < face_dofs; i++)
|
||||
{
|
||||
const int ni = (dof_map.Size()==0) ? i : dof_map[i];
|
||||
@@ -1602,7 +1611,7 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
|
||||
{
|
||||
// master side is elem 2, so we permute to order dofs as elem 1.
|
||||
li = PermuteFaceL2(dim, face_id2, face_id1,
|
||||
orientation, dof1d, li);
|
||||
orientation_i, dof1d, li);
|
||||
}
|
||||
for (int j = 0; j < face_dofs; j++)
|
||||
{
|
||||
@@ -1611,7 +1620,7 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
|
||||
{
|
||||
// master side is elem 2, so we permute to order dofs as elem 1.
|
||||
lj = PermuteFaceL2(dim, face_id2, face_id1,
|
||||
orientation, dof1d, lj);
|
||||
orientation_j, dof1d, lj);
|
||||
}
|
||||
const int nj = (dof_map.Size()==0) ? j : dof_map[j];
|
||||
(*interpolator)(li,lj) = native_interpolator(ni,nj);
|
||||
@@ -1676,7 +1685,7 @@ NCL2FaceRestriction::NCL2FaceRestriction(const FiniteElementSpace &fes,
|
||||
const L2FaceValues m,
|
||||
bool build)
|
||||
: L2FaceRestriction(fes, f_ordering, type, m, false),
|
||||
interpolations(fes, f_ordering, type)
|
||||
interpolations(fes.GetInterpolationManager(ordering, type))
|
||||
{
|
||||
if (!build) { return; }
|
||||
x_interp.UseDevice(true);
|
||||
@@ -2202,14 +2211,6 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
|
||||
}
|
||||
if ( face.IsConforming() )
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
}
|
||||
else // Non-conforming face
|
||||
{
|
||||
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
|
||||
}
|
||||
f_ind++;
|
||||
}
|
||||
else if ( type==FaceType::Boundary && face.IsBoundary() )
|
||||
@@ -2219,7 +2220,6 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
SetBoundaryDofsScatterIndices2(face,f_ind);
|
||||
}
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
f_ind++;
|
||||
}
|
||||
}
|
||||
@@ -2232,10 +2232,6 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
gather_offsets[i] += gather_offsets[i - 1];
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into a contiguous memory structure.
|
||||
interpolations.LinearizeInterpolatorMapIntoVector();
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void NCL2FaceRestriction::ComputeGatherIndices()
|
||||
@@ -2278,6 +2274,18 @@ void NCL2FaceRestriction::ComputeGatherIndices()
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
static int GetSharedVSize(const FiniteElementSpace &fes)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes))
|
||||
{
|
||||
const_cast<ParFiniteElementSpace*>(pfes)->ExchangeFaceNbrData();
|
||||
return pfes->GetFaceNbrVSize();
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
|
||||
const FiniteElementSpace& fes_,
|
||||
const ElementDofOrdering ordering_,
|
||||
@@ -2288,25 +2296,54 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
|
||||
nfaces(fes.GetNFbyType(type)),
|
||||
vdim(fes.GetVDim()),
|
||||
byvdim(fes.GetOrdering() == Ordering::byVDIM),
|
||||
face_dofs(nfaces > 0 ? fes.GetFaceElement(0)->GetDof() : 0),
|
||||
face_dofs(fes.GetTypicalTraceElement()->GetDof()),
|
||||
nfdofs(face_dofs*nfaces),
|
||||
ndofs(fes.GetNDofs())
|
||||
ndofs(fes.GetNDofs()),
|
||||
nsdofs(GetSharedVSize(fes))
|
||||
{
|
||||
height = nfdofs;
|
||||
width = ndofs;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes);
|
||||
#endif
|
||||
|
||||
const Table &face2dof = fes.GetFaceToDofTable();
|
||||
|
||||
const Mesh &mesh = *fes.GetMesh();
|
||||
int face_idx = 0;
|
||||
gather_map.SetSize(nfdofs);
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
scatter_map.SetSize(nfdofs);
|
||||
gather_map.SetSize(ndofs + nsdofs);
|
||||
gather_map = -1;
|
||||
|
||||
Array<int> dofs;
|
||||
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
|
||||
if (!face.IsOfFaceType(type)) { continue; }
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse()) { continue; }
|
||||
|
||||
if (f < mesh.GetNumFaces())
|
||||
{
|
||||
gather_map[i + face_idx*face_dofs] = face2dof.GetJ()[i + f*face_dofs];
|
||||
// Local face
|
||||
face2dof.GetRow(f, dofs);
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
{
|
||||
scatter_map[i + face_idx*face_dofs] = dofs[i];
|
||||
gather_map[dofs[i]] = i + face_idx*face_dofs;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shared (non-conforming) ghost face
|
||||
#ifdef MFEM_USE_MPI
|
||||
MFEM_ASSERT(pfes != nullptr, "");
|
||||
pfes->GetFaceNbrFaceVDofs(f, dofs);
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
{
|
||||
scatter_map[i + face_idx*face_dofs] = ndofs + dofs[i];
|
||||
gather_map[ndofs + dofs[i]] = i + face_idx*face_dofs;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
++face_idx;
|
||||
}
|
||||
@@ -2314,13 +2351,19 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
|
||||
|
||||
void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
const int NDOFS = ndofs;
|
||||
const int nd = face_dofs;
|
||||
const int nf = nfaces;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int *map = gather_map.Read();
|
||||
const int *map = scatter_map.Read();
|
||||
|
||||
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
|
||||
MFEM_ASSERT(face_nbr_data.Size() / vd == nsdofs, "");
|
||||
|
||||
const auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
|
||||
const auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:nsdofs, t?nsdofs:vd);
|
||||
auto d_y = Reshape(y.Write(), nd, vd, nf);
|
||||
|
||||
mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
|
||||
@@ -2328,7 +2371,8 @@ void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
|
||||
const int j = map[i];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
|
||||
if (j < NDOFS) { d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c); }
|
||||
else { d_y(i % nd, c, i / nd) = d_x_shared(t?c:(j-NDOFS), t?(j-NDOFS):c); }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2343,15 +2387,39 @@ void L2InterfaceFaceRestriction::AddMultTranspose(
|
||||
const int *map = gather_map.Read();
|
||||
|
||||
const auto d_x = Reshape(x.Read(), nd, vd, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
|
||||
|
||||
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i) { d_y[i] = 0.0; });
|
||||
mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
|
||||
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int j = map[i];
|
||||
if (j < 0) { return; }
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(t?c:j, t?j:c) = d_x(i % nd, c, i / nd);
|
||||
d_y(t?c:i, t?i:c) += a*d_x(j % nd, c, j / nd);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void L2InterfaceFaceRestriction::MultTransposeShared(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
const int nd = face_dofs;
|
||||
const int nf = nfaces;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int *map = gather_map.Read();
|
||||
|
||||
const auto d_x = Reshape(x.Read(), nd, vd, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:(ndofs+nsdofs), t?(ndofs+nsdofs):vd);
|
||||
y = 0.0;
|
||||
|
||||
mfem::forall(ndofs + nsdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int j = map[i];
|
||||
if (j < 0) { return; }
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(t?c:i, t?i:c) = d_x(j % nd, c, j / nd);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2361,6 +2429,11 @@ const Array<int> &L2InterfaceFaceRestriction::GatherMap() const
|
||||
return gather_map;
|
||||
}
|
||||
|
||||
const Array<int> &L2InterfaceFaceRestriction::ScatterMap() const
|
||||
{
|
||||
return scatter_map;
|
||||
}
|
||||
|
||||
Vector GetLVectorFaceNbrData(
|
||||
const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
|
||||
{
|
||||
@@ -2382,6 +2455,7 @@ Vector GetLVectorFaceNbrData(
|
||||
{
|
||||
ParGridFunction gf(pfes, const_cast<Vector&>(x));
|
||||
gf.ExchangeFaceNbrData();
|
||||
x.SyncMemory(gf);
|
||||
return std::move(gf.FaceNbrData());
|
||||
}
|
||||
}
|
||||
|
||||
+26
-14
@@ -812,13 +812,12 @@ protected:
|
||||
PointMatrix and a local face identifier. */
|
||||
using Key = std::pair<const DenseMatrix*,int>;
|
||||
/// The temporary map used to store the different interpolators.
|
||||
using Map = std::map<Key, std::pair<int,const DenseMatrix*>>;
|
||||
using Map =
|
||||
std::unordered_map<Key, std::pair<int,const DenseMatrix*>, PairHasher>;
|
||||
Map interp_map; // The temporary map that stores the interpolators.
|
||||
|
||||
public:
|
||||
InterpolationManager() = delete;
|
||||
|
||||
/** @brief main constructor.
|
||||
/** @brief Constructor.
|
||||
|
||||
@param[in] fes The FiniteElementSpace on which this operates
|
||||
@param[in] ordering Request a specific element ordering.
|
||||
@@ -909,7 +908,7 @@ private:
|
||||
class NCL2FaceRestriction : virtual public L2FaceRestriction
|
||||
{
|
||||
protected:
|
||||
InterpolationManager interpolations;
|
||||
const InterpolationManager &interpolations;
|
||||
mutable Vector x_interp;
|
||||
|
||||
/** @brief Constructs an NCL2FaceRestriction, this is a specialization of a
|
||||
@@ -996,9 +995,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillI(SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
@@ -1016,9 +1013,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillJAndData(const Vector &fea_data,
|
||||
SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
@@ -1036,9 +1031,7 @@ public:
|
||||
added the face contributions.
|
||||
The format is: dofs x dofs x ne, where dofs is the
|
||||
number of dofs per element and ne the number of
|
||||
elements.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
elements. */
|
||||
void AddFaceMatricesToElementMatrices(const Vector &fea_data,
|
||||
Vector &ea_data) const override;
|
||||
|
||||
@@ -1130,7 +1123,9 @@ protected:
|
||||
const int face_dofs; ///< Number of dofs on each face
|
||||
const int nfdofs; ///< Total number of dofs on the faces (E-vector size)
|
||||
const int ndofs; ///< Number of dofs in the space (L-vector size)
|
||||
const int nsdofs; ///< Number of shared face neighbor (ghost) dofs
|
||||
Array<int> gather_map; ///< Gather map
|
||||
Array<int> scatter_map; ///< Scatter map
|
||||
|
||||
public:
|
||||
/** @brief Constructs an L2InterfaceFaceRestriction.
|
||||
@@ -1168,7 +1163,24 @@ public:
|
||||
void AddMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const override;
|
||||
|
||||
/// @brief Gather degrees of freedom, from face E-vector to L-vector and
|
||||
/// shared (ghost) DOFs.
|
||||
///
|
||||
/// @param[in] x The face E-Vector degrees of freedom with size
|
||||
/// (face_dofs, vdim, nf), where nf is the number of
|
||||
/// interior or boundary faces requested by @a type in the
|
||||
/// constructor. The face_dofs should be ordered according
|
||||
/// to the given ElementDofOrdering
|
||||
/// @param[out] y Vector of length vsize + face neighbor vsize
|
||||
void MultTransposeShared(const Vector &x, Vector &y) const;
|
||||
|
||||
const Array<int> &GatherMap() const override;
|
||||
|
||||
/// @brief Return the low-level mapping from L-dofs to E-dofs.
|
||||
///
|
||||
/// L-dofs that do not correspond to an E-dof (e.g. that lie on a face of a
|
||||
/// different type) are given index -1.
|
||||
const Array<int> &ScatterMap() const;
|
||||
};
|
||||
|
||||
/** @brief Convert a dof face index from Native ordering to lexicographic
|
||||
|
||||
+5
-2
@@ -4102,8 +4102,11 @@ void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &d_loc,
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Don't count the overlapping DOFs in parallel.
|
||||
// The pfes might be ordered byVDIM, while the loop goes consecutively.
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
if (parallel)
|
||||
{
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
}
|
||||
#endif
|
||||
|
||||
dof_cnt++;
|
||||
|
||||
+31
-12
@@ -333,6 +333,12 @@ void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
|
||||
int nel_ho = mesh_ho->GetNE();
|
||||
int nel_lor = mesh_lor->GetNE();
|
||||
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
M_LH.SetSize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
|
||||
|
||||
int nref_max = 0;
|
||||
@@ -831,11 +837,17 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::Mult(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMult(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int iho = 0;
|
||||
const int nref = ho2lor.RowSize(iho);
|
||||
const int ndof_ho = fes_ho.GetFE(iho)->GetDof();
|
||||
const int ndof_lor = fes_lor.GetFE(ho2lor.GetRow(iho)[0])->GetDof();
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
DenseTensor R_dt;
|
||||
R_dt.NewMemoryAndSize(R.GetMemory(), ndof_lor*nref, ndof_ho, nel_ho, false);
|
||||
@@ -887,11 +899,17 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::MultTranspose(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int iho = 0;
|
||||
const int nref = ho2lor.RowSize(iho);
|
||||
const int ndof_ho = fes_ho.GetFE(iho)->GetDof();
|
||||
const int ndof_lor = fes_lor.GetFE(ho2lor.GetRow(iho)[0])->GetDof();
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
DenseTensor R_dt;
|
||||
R_dt.NewMemoryAndSize(R.GetMemory(), ndof_lor*nref, ndof_ho, nel_ho, false);
|
||||
@@ -901,7 +919,6 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::Prolongate(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
|
||||
if (fes_ho.GetNE() == 0) { return; }
|
||||
|
||||
if (use_ea)
|
||||
@@ -960,14 +977,13 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAProlongate(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::ProlongateTranspose(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
if (fes_ho.GetNE() == 0) { return; }
|
||||
|
||||
if (use_ea)
|
||||
{
|
||||
return EAProlongateTranspose(x,y);
|
||||
}
|
||||
|
||||
|
||||
if (fes_ho.GetNE() == 0) { return; }
|
||||
MFEM_VERIFY(P.Size() > 0, "Prolongation not supported for these spaces.")
|
||||
int vdim = fes_ho.GetVDim();
|
||||
Array<int> vdofs;
|
||||
@@ -1244,13 +1260,6 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space
|
||||
int ndof_ho = pfes_ho.GetNDofs();
|
||||
int ndof_lor = pfes_lor.GetNDofs();
|
||||
|
||||
|
||||
// If the local mesh is empty, skip all computations
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
|
||||
|
||||
int nref_max = 0;
|
||||
@@ -1860,6 +1869,11 @@ L2ProjectionGridTransfer::H1SpaceMixedMassOperator::H1SpaceMixedMassOperator(
|
||||
void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::Mult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
if (fes_ho->GetNE() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Operator* elem_restrict_ho = fes_ho->GetElementRestriction(
|
||||
ElementDofOrdering::NATIVE);
|
||||
const Operator* elem_restrict_lor = fes_lor->GetElementRestriction(
|
||||
@@ -1906,6 +1920,11 @@ void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::Mult(const Vector &x,
|
||||
void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::MultTranspose(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
if (fes_ho->GetNE() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Operator* elem_restrict_ho = fes_ho->GetElementRestriction(
|
||||
ElementDofOrdering::NATIVE);
|
||||
const Operator* elem_restrict_lor = fes_lor->GetElementRestriction(
|
||||
|
||||
@@ -18,6 +18,7 @@ list(APPEND SRCS
|
||||
gecko.cpp
|
||||
globals.cpp
|
||||
hash.cpp
|
||||
hash_util.cpp
|
||||
isockstream.cpp
|
||||
mem_manager.cpp
|
||||
occa.cpp
|
||||
@@ -46,6 +47,7 @@ list(APPEND HDRS
|
||||
globals.hpp
|
||||
zstr.hpp
|
||||
hash.hpp
|
||||
hash_util.hpp
|
||||
isockstream.hpp
|
||||
kdtree.hpp
|
||||
mem_alloc.hpp
|
||||
|
||||
+24
-1
@@ -114,10 +114,22 @@ public:
|
||||
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
|
||||
|
||||
/// Move assignment operator
|
||||
/** If *this is a non-owning view (e.g., from MakeRef()), the data is copied
|
||||
so that the base is also modified. */
|
||||
Array<T> &operator=(Array<T> &&src)
|
||||
{
|
||||
if (this == &src) { return *this; }
|
||||
Swap(src); // Swap does not use move assignment!
|
||||
// If *this is a non-owning view (alias), and its capacity is sufficient
|
||||
// to contain src, then copy into *this so that the alias's base memory is
|
||||
// modified.
|
||||
if (!OwnsData() && Capacity() >= src.Size())
|
||||
{
|
||||
*this = src; // Copy assignment.
|
||||
}
|
||||
else
|
||||
{
|
||||
Swap(src); // Swap the pointers only.
|
||||
}
|
||||
src.DeleteAll();
|
||||
return *this;
|
||||
}
|
||||
@@ -251,6 +263,9 @@ public:
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
|
||||
/// Make this Array a reference to the given sub-Memory of @a base.
|
||||
inline void MakeRef(Memory<T> &base, int offset, int size_);
|
||||
|
||||
/// Reset the Array to use the given external Memory @a mem and size @a s.
|
||||
/** If @a own_mem is false, the Array will not own any of the pointers of
|
||||
@a mem.
|
||||
@@ -1073,6 +1088,14 @@ inline void Array<T>::MakeRef(const Array &master)
|
||||
data.MakeAlias(master.GetMemory(), 0, size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(Memory<T> &base, int offset, int size_)
|
||||
{
|
||||
data.Delete();
|
||||
size = size_;
|
||||
data.MakeAlias(base, offset, size_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::NewMemoryAndSize(
|
||||
const Memory<T> &mem, int s, bool own_mem)
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#endif
|
||||
|
||||
#if !defined(MFEM_USE_CUDA_OR_HIP)
|
||||
constexpr bool mfem_use_gpu = false;
|
||||
#define MFEM_DEVICE
|
||||
#define MFEM_HOST
|
||||
#define MFEM_LAMBDA
|
||||
@@ -52,6 +53,7 @@
|
||||
#define MFEM_DEVICE_SYNC
|
||||
// MFEM_STREAM_SYNC is used for UVM and MPI GPU-Aware kernels
|
||||
#define MFEM_STREAM_SYNC
|
||||
#define MFEM_LAUNCH_BOUNDS(...)
|
||||
#endif
|
||||
|
||||
#if !((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
|
||||
#define MFEM_USE_CUDA_OR_HIP
|
||||
constexpr bool mfem_use_gpu = true;
|
||||
#define MFEM_DEVICE __device__
|
||||
#define MFEM_HOST __host__
|
||||
#define MFEM_LAMBDA __host__
|
||||
#define MFEM_LAUNCH_BOUNDS __launch_bounds__
|
||||
// #define MFEM_HOST_DEVICE __host__ __device__ // defined in config/config.hpp
|
||||
#define MFEM_DEVICE_SYNC MFEM_GPU_CHECK(cudaDeviceSynchronize())
|
||||
#define MFEM_STREAM_SYNC MFEM_GPU_CHECK(cudaStreamSynchronize(0))
|
||||
|
||||
+207
-44
@@ -295,11 +295,12 @@ using hip_threads_z =
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA) && defined(__CUDACC__)
|
||||
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
void RajaCuWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
//true denotes asynchronous kernel
|
||||
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
|
||||
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS,true>>(RAJA::RangeSegment(0,N),
|
||||
d_body);
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
@@ -362,18 +363,18 @@ struct RajaCuWrap;
|
||||
template <>
|
||||
struct RajaCuWrap<1>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaCuWrap1D<BLCK>(N, d_body);
|
||||
RajaCuWrap1D(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<2>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -384,7 +385,7 @@ struct RajaCuWrap<2>
|
||||
template <>
|
||||
struct RajaCuWrap<3>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -395,11 +396,12 @@ struct RajaCuWrap<3>
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
|
||||
template <const int BLOCKS = MFEM_HIP_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
void RajaHipWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
//true denotes asynchronous kernel
|
||||
RAJA::forall<RAJA::hip_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
|
||||
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true>>(RAJA::RangeSegment(0,N),
|
||||
d_body);
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
@@ -462,18 +464,18 @@ struct RajaHipWrap;
|
||||
template <>
|
||||
struct RajaHipWrap<1>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaHipWrap1D<BLCK>(N, d_body);
|
||||
RajaHipWrap1D(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<2>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -484,7 +486,7 @@ struct RajaHipWrap<2>
|
||||
template <>
|
||||
struct RajaHipWrap<3>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -584,12 +586,31 @@ void CuKernel2D(const int N, BODY body)
|
||||
body(k);
|
||||
}
|
||||
|
||||
// __launch_bounds__ second argument is omitted to get the default behavior
|
||||
template <int MAX_THREADS_PER_BLOCK, typename BODY>
|
||||
__global__
|
||||
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
|
||||
static void CuKernel2DLaunchBounds(const int N, BODY body)
|
||||
{
|
||||
const int k = blockIdx.x*blockDim.z + threadIdx.z;
|
||||
if (k >= N) { return; }
|
||||
body(k);
|
||||
}
|
||||
|
||||
template <typename BODY> __global__ static
|
||||
void CuKernel3D(const int N, BODY body)
|
||||
{
|
||||
for (int k = blockIdx.x; k < N; k += gridDim.x) { body(k); }
|
||||
}
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK, typename BODY>
|
||||
__global__
|
||||
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
|
||||
static void CuKernel3DLaunchBounds(const int N, BODY body)
|
||||
{
|
||||
for (int k = blockIdx.x; k < N; k += gridDim.x) { body(k); }
|
||||
}
|
||||
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
void CuWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
@@ -604,6 +625,8 @@ void CuWrap2D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
// required for optimized GCC/NVCC builds to prevent runtime
|
||||
// ODR/linkage violations of inlined templated kernel helpers
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int GRID = (N+BZ-1)/BZ;
|
||||
const dim3 BLCK(X,Y,BZ);
|
||||
@@ -611,6 +634,19 @@ void CuWrap2D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
|
||||
void CuWrap2DLaunchBounds(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int GRID = (N+BZ-1)/BZ;
|
||||
const dim3 BLCK(X,Y,BZ);
|
||||
static_assert(MAX_THREADS_PER_BLOCK > 0);
|
||||
CuKernel2DLaunchBounds<MAX_THREADS_PER_BLOCK><<<GRID,BLCK>>>(N, d_body);
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void CuWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
@@ -622,24 +658,35 @@ void CuWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
struct CuWrap;
|
||||
|
||||
template <>
|
||||
struct CuWrap<1>
|
||||
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
|
||||
void CuWrap3DLaunchBounds(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
if (N==0) { return; }
|
||||
const int GRID = G == 0 ? N : G;
|
||||
const dim3 BLCK(X,Y,Z);
|
||||
static_assert(MAX_THREADS_PER_BLOCK > 0);
|
||||
CuKernel3DLaunchBounds<MAX_THREADS_PER_BLOCK><<<GRID, BLCK>>>(N, d_body);
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim, int MAX_THREADS_PER_BLOCK> struct CuWrap;
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct CuWrap<1, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
CuWrap1D<BLCK>(N, d_body);
|
||||
CuWrap1D<MFEM_CUDA_BLOCKS>(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CuWrap<2>
|
||||
struct CuWrap<2, 0>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -647,10 +694,22 @@ struct CuWrap<2>
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CuWrap<3>
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct CuWrap<2, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
static_assert(MAX_THREADS_PER_BLOCK > 0);
|
||||
CuWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CuWrap<3, 0>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -658,6 +717,17 @@ struct CuWrap<3>
|
||||
}
|
||||
};
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct CuWrap<3, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
CuWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // defined(MFEM_USE_CUDA) && defined(__CUDACC__)
|
||||
|
||||
|
||||
@@ -680,13 +750,31 @@ void HipKernel2D(const int N, BODY body)
|
||||
body(k);
|
||||
}
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK, typename BODY>
|
||||
__global__
|
||||
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
|
||||
static void HipKernel2DLaunchBounds(const int N, BODY body)
|
||||
{
|
||||
const int k = hipBlockIdx_x*hipBlockDim_z + hipThreadIdx_z;
|
||||
if (k >= N) { return; }
|
||||
body(k);
|
||||
}
|
||||
|
||||
template <typename BODY> __global__ static
|
||||
void HipKernel3D(const int N, BODY body)
|
||||
{
|
||||
for (int k = hipBlockIdx_x; k < N; k += hipGridDim_x) { body(k); }
|
||||
}
|
||||
|
||||
template <const int BLCK = MFEM_HIP_BLOCKS, typename DBODY>
|
||||
template <int MAX_THREADS_PER_BLOCK, typename BODY>
|
||||
__global__
|
||||
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
|
||||
static void HipKernel3DLaunchBounds(const int N, BODY body)
|
||||
{
|
||||
for (int k = hipBlockIdx_x; k < N; k += hipGridDim_x) { body(k); }
|
||||
}
|
||||
|
||||
template <int BLCK = MFEM_HIP_BLOCKS, typename DBODY>
|
||||
void HipWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
@@ -700,12 +788,27 @@ void HipWrap2D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int GRID = (N+BZ-1)/BZ;
|
||||
const dim3 BLCK(X,Y,BZ);
|
||||
hipLaunchKernelGGL(HipKernel2D,GRID,BLCK,0,nullptr,N,d_body);
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
|
||||
void HipWrap2DLaunchBounds(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int GRID = (N+BZ-1)/BZ;
|
||||
const dim3 BLCK(X,Y,BZ);
|
||||
static_assert(MAX_THREADS_PER_BLOCK > 0);
|
||||
HipKernel2DLaunchBounds<MAX_THREADS_PER_BLOCK><<<dim3(GRID), dim3(BLCK), 0, 0>>>
|
||||
(N, d_body);
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void HipWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
@@ -717,24 +820,36 @@ void HipWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
struct HipWrap;
|
||||
|
||||
template <>
|
||||
struct HipWrap<1>
|
||||
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
|
||||
void HipWrap3DLaunchBounds(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
if (N==0) { return; }
|
||||
const int GRID = G == 0 ? N : G;
|
||||
const dim3 BLCK(X,Y,Z);
|
||||
static_assert(MAX_THREADS_PER_BLOCK > 0);
|
||||
HipKernel3DLaunchBounds<MAX_THREADS_PER_BLOCK><<<dim3(GRID), dim3(BLCK), 0, 0>>>
|
||||
(N, d_body);
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim, int MAX_THREADS_PER_BLOCK> struct HipWrap;
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct HipWrap<1, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
HipWrap1D<BLCK>(N, d_body);
|
||||
HipWrap1D<MFEM_HIP_BLOCKS>(N, d_body);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HipWrap<2>
|
||||
struct HipWrap<2, 0>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -742,10 +857,21 @@ struct HipWrap<2>
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HipWrap<3>
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct HipWrap<2, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
HipWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HipWrap<3, 0>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
@@ -753,11 +879,24 @@ struct HipWrap<3>
|
||||
}
|
||||
};
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct HipWrap<3, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
HipWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // defined(MFEM_USE_HIP) && defined(__HIP__)
|
||||
|
||||
|
||||
/// The forall kernel body wrapper
|
||||
template <const int DIM, typename d_lambda, typename h_lambda>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Forall host & device kernel dispatch
|
||||
template <int DIM, int MAX_THREADS_PER_BLOCK = 0,
|
||||
typename d_lambda, typename h_lambda>
|
||||
inline void ForallWrap(const bool use_dev, const int N,
|
||||
d_lambda &&d_body, h_lambda &&h_body,
|
||||
const int X=0, const int Y=0, const int Z=0,
|
||||
@@ -790,7 +929,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::CUDA is allowed, use it
|
||||
if (Device::Allows(Backend::CUDA))
|
||||
{
|
||||
return CuWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
return CuWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -798,7 +937,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::HIP is allowed, use it
|
||||
if (Device::Allows(Backend::HIP))
|
||||
{
|
||||
return HipWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
return HipWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -827,7 +966,9 @@ backend_cpu:
|
||||
for (int k = 0; k < N; k++) { h_body(k); }
|
||||
}
|
||||
|
||||
template <const int DIM, typename lambda>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Forall host & device kernel wrappers
|
||||
template <int DIM, typename lambda>
|
||||
inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
|
||||
const int X=0, const int Y=0, const int Z=0,
|
||||
const int G=0)
|
||||
@@ -835,6 +976,16 @@ inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
|
||||
ForallWrap<DIM>(use_dev, N, body, body, X, Y, Z, G);
|
||||
}
|
||||
|
||||
template <int DIM, int MAX_THREADS_PER_BLOCK, typename lambda>
|
||||
inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
|
||||
const int X=0, const int Y=0, const int Z=0,
|
||||
const int G=0)
|
||||
{
|
||||
ForallWrap<DIM, MAX_THREADS_PER_BLOCK>(use_dev, N, body, body, X, Y, Z, G);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// forall interfaces
|
||||
template<typename lambda>
|
||||
inline void forall(int N, lambda &&body) { ForallWrap<1>(true, N, body); }
|
||||
|
||||
@@ -843,7 +994,7 @@ inline void forall(int Nx, int Ny, lambda &&body)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
|
||||
mfem::forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
|
||||
{
|
||||
int j = idx / Nx;
|
||||
int i = idx % Nx;
|
||||
@@ -879,7 +1030,7 @@ inline void forall(int Nx, int Ny, int Nz, lambda &&body)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
|
||||
mfem::forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
|
||||
{
|
||||
int i = idx % Nx;
|
||||
int j = idx / Nx;
|
||||
@@ -927,6 +1078,12 @@ inline void forall_2D(int N, int X, int Y, lambda &&body)
|
||||
ForallWrap<2>(true, N, body, X, Y, 1);
|
||||
}
|
||||
|
||||
template<int MAX_THREADS_PER_BLOCK, typename lambda>
|
||||
inline void forall_2D(int N, int X, int Y, lambda &&body)
|
||||
{
|
||||
ForallWrap<2, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, 1);
|
||||
}
|
||||
|
||||
template<typename lambda>
|
||||
inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
|
||||
{
|
||||
@@ -939,6 +1096,12 @@ inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
|
||||
ForallWrap<3>(true, N, body, X, Y, Z, 0);
|
||||
}
|
||||
|
||||
template<int MAX_THREADS_PER_BLOCK, typename lambda>
|
||||
inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
|
||||
{
|
||||
ForallWrap<3, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, Z, 0);
|
||||
}
|
||||
|
||||
template<typename lambda>
|
||||
inline void forall_3D_grid(int N, int X, int Y, int Z, int G, lambda &&body)
|
||||
{
|
||||
|
||||
@@ -113,6 +113,10 @@ void SetGlobalMPI_Comm(MPI_Comm comm);
|
||||
/// to suppress the warning.
|
||||
const char* GetEnv(const char* name);
|
||||
|
||||
/// Signed indices i -> -1 - i are used as a convention to encode orientation.
|
||||
inline MFEM_HOST_DEVICE int FlipIndexSign(int i) { return -1 - i; }
|
||||
inline MFEM_HOST_DEVICE int UnsignIndex(int i) { return i < 0 ? -1 - i : i; }
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -80,159 +80,4 @@ std::string HashFunction::GetHash() const
|
||||
return hash;
|
||||
}
|
||||
|
||||
constexpr static uint64_t rotl64(uint64_t x, int r)
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
void Hasher::init(uint64_t seed)
|
||||
{
|
||||
data[0] = seed;
|
||||
data[1] = seed;
|
||||
nbytes = 0;
|
||||
}
|
||||
|
||||
void Hasher::add_block(uint64_t k1, uint64_t k2)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] = rotl64(data[0], 27);
|
||||
data[0] += data[1];
|
||||
data[0] = data[0] * 5 + 0x52dce729ull;
|
||||
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
data[1] = rotl64(data[1], 31);
|
||||
data[1] += data[0];
|
||||
data[1] = data[1] * 5 + 0x38495ab5ull;
|
||||
}
|
||||
|
||||
static uint64_t fmix64(uint64_t k)
|
||||
{
|
||||
// http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
|
||||
// mix13
|
||||
k ^= k >> 30;
|
||||
k *= 0xbf58476d1ce4e5b9ull;
|
||||
k ^= k >> 27;
|
||||
k *= 0x94d049bb133111ebull;
|
||||
k ^= k >> 31;
|
||||
return k;
|
||||
}
|
||||
|
||||
void Hasher::append(const uint8_t *vs, uint64_t bytes)
|
||||
{
|
||||
if (bytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto rem = nbytes % 16;
|
||||
nbytes += bytes;
|
||||
uint8_t *tmp = reinterpret_cast<uint8_t *>(buf_);
|
||||
while (true)
|
||||
{
|
||||
if (bytes + rem >= 16)
|
||||
{
|
||||
std::copy(vs, vs + 16 - rem, tmp + rem);
|
||||
add_block(buf_[0], buf_[1]);
|
||||
vs += (16 - rem);
|
||||
bytes -= (16 - rem);
|
||||
rem = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(vs, vs + bytes, tmp + rem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hasher::finalize()
|
||||
{
|
||||
auto rem = nbytes % 16;
|
||||
if (rem > 0)
|
||||
{
|
||||
nbytes -= rem;
|
||||
if (rem <= 8)
|
||||
{
|
||||
finalize(buf_[0], rem);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalize(buf_[0], buf_[1], rem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, uint64_t k2, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+1
-70
@@ -15,8 +15,8 @@
|
||||
#include "../config/config.hpp"
|
||||
#include "array.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "hash_util.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -457,75 +457,6 @@ protected:
|
||||
int BinSize(int idx) const;
|
||||
};
|
||||
|
||||
///
|
||||
/// @brief streaming implementation for murmurhash3 128 (x64).
|
||||
/// Constructs the hash in 3 stages: init, append, finalize.
|
||||
///
|
||||
struct Hasher
|
||||
{
|
||||
/// where the final hash result is stored after finalize. Use data[1] when
|
||||
/// only 64 bits are required.
|
||||
uint64_t data[2] = {0, 0};
|
||||
|
||||
private:
|
||||
uint64_t nbytes = 0;
|
||||
|
||||
uint64_t buf_[2] = {0, 0};
|
||||
|
||||
public:
|
||||
|
||||
/// resets this hasher back to an initial seed
|
||||
void init(uint64_t seed = 0);
|
||||
void append(const uint8_t *vs, uint64_t bytes);
|
||||
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
// add 16 bytes
|
||||
void add_block(uint64_t k1, uint64_t k2);
|
||||
|
||||
// add [1-8] more bytes, then finalize
|
||||
void finalize(uint64_t k1, int num);
|
||||
|
||||
// add [1-15] more bytes, then finalize
|
||||
// 0 < num < 16
|
||||
void finalize(uint64_t k1, uint64_t k2, int num);
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::pair. Usable in place of std::hash<std::pair<T,U>>
|
||||
struct PairHasher
|
||||
{
|
||||
template <class T, class V>
|
||||
size_t operator()(const std::pair<T, V> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
hash.append(reinterpret_cast<const uint8_t *>(&v.first), sizeof(T));
|
||||
hash.append(reinterpret_cast<const uint8_t *>(&v.second), sizeof(V));
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::array. Usable in place of std::hash<std::array<T,N>>
|
||||
struct ArrayHasher
|
||||
{
|
||||
template <class T, size_t N>
|
||||
size_t operator()(const std::array<T, N> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
hash.append(reinterpret_cast<const uint8_t *>(&v[i]), sizeof(T));
|
||||
}
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash function for data sequences.
|
||||
/** Depends on GnuTLS for SHA-256 hashing. */
|
||||
class HashFunction
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "hash_util.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
constexpr static uint64_t rotl64(uint64_t x, int r)
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
void Hasher::init(uint64_t seed)
|
||||
{
|
||||
data[0] = seed;
|
||||
data[1] = seed;
|
||||
nbytes = 0;
|
||||
}
|
||||
|
||||
void Hasher::add_block(uint64_t k1, uint64_t k2)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] = rotl64(data[0], 27);
|
||||
data[0] += data[1];
|
||||
data[0] = data[0] * 5 + 0x52dce729ull;
|
||||
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
data[1] = rotl64(data[1], 31);
|
||||
data[1] += data[0];
|
||||
data[1] = data[1] * 5 + 0x38495ab5ull;
|
||||
}
|
||||
|
||||
static uint64_t fmix64(uint64_t k)
|
||||
{
|
||||
// http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
|
||||
// mix13
|
||||
k ^= k >> 30;
|
||||
k *= 0xbf58476d1ce4e5b9ull;
|
||||
k ^= k >> 27;
|
||||
k *= 0x94d049bb133111ebull;
|
||||
k ^= k >> 31;
|
||||
return k;
|
||||
}
|
||||
|
||||
void Hasher::append(const std::byte *vs, uint64_t bytes)
|
||||
{
|
||||
if (bytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto rem = nbytes % 16;
|
||||
nbytes += bytes;
|
||||
std::byte *tmp = reinterpret_cast<std::byte *>(buf_);
|
||||
while (true)
|
||||
{
|
||||
if (bytes + rem >= 16)
|
||||
{
|
||||
std::copy(vs, vs + 16 - rem, tmp + rem);
|
||||
add_block(buf_[0], buf_[1]);
|
||||
vs += (16 - rem);
|
||||
bytes -= (16 - rem);
|
||||
rem = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(vs, vs + bytes, tmp + rem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hasher::finalize()
|
||||
{
|
||||
auto rem = nbytes % 16;
|
||||
if (rem > 0)
|
||||
{
|
||||
nbytes -= rem;
|
||||
if (rem <= 8)
|
||||
{
|
||||
finalize(buf_[0], rem);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalize(buf_[0], buf_[1], rem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, uint64_t k2, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_HASH_UTIL_HPP
|
||||
#define MFEM_HASH_UTIL_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// @brief streaming implementation for murmurhash3 128 (x64).
|
||||
///
|
||||
/// Constructs the hash in 3 stages: init, append, finalize.
|
||||
struct Hasher
|
||||
{
|
||||
/// @brief Storage for the final hash result after finalize() is called.
|
||||
///
|
||||
/// Use data[1] when only 64 bits are required.
|
||||
uint64_t data[2] = {0, 0};
|
||||
|
||||
private:
|
||||
uint64_t nbytes = 0;
|
||||
uint64_t buf_[2] = {0, 0};
|
||||
|
||||
public:
|
||||
|
||||
/// Resets the Hasher back to an initial seed
|
||||
void init(uint64_t seed = 0);
|
||||
|
||||
/// Append data @a vs of size @a bytes.
|
||||
void append(const std::byte *vs, uint64_t bytes);
|
||||
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
/// Add a block of 16 bytes.
|
||||
void add_block(uint64_t k1, uint64_t k2);
|
||||
|
||||
/// @brief Add [1-8] more bytes, then finalize.
|
||||
///
|
||||
/// @a num must satisfy 0 < num < 9.
|
||||
void finalize(uint64_t k1, int num);
|
||||
|
||||
/// @brief Add [1-15] more bytes, then finalize.
|
||||
///
|
||||
/// @a num must satisfy 0 < num < 16.
|
||||
void finalize(uint64_t k1, uint64_t k2, int num);
|
||||
};
|
||||
|
||||
template <class T> struct ChainedHasher
|
||||
{
|
||||
static void Append(Hasher &hasher, const T &value)
|
||||
{
|
||||
if constexpr (std::is_fundamental_v<T> || std::is_pointer_v<T>)
|
||||
{
|
||||
hasher.append(reinterpret_cast<const std::byte *>(&value), sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::hash<T> h;
|
||||
auto v = h(value);
|
||||
hasher.append(reinterpret_cast<std::byte *>(&v), sizeof(v));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class V> struct ChainedHasher<std::pair<T, V>>
|
||||
{
|
||||
static void Append(Hasher &hasher, const std::pair<T, V> &value)
|
||||
{
|
||||
ChainedHasher<T>::Append(hasher, value.first);
|
||||
ChainedHasher<V>::Append(hasher, value.second);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, size_t N> struct ChainedHasher<std::array<T, N>>
|
||||
{
|
||||
static void Append(Hasher &hasher, const std::array<T, N> &value)
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
ChainedHasher<T>::Append(hasher, value[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class... Ts> struct ChainedHasher<std::tuple<Ts...>>
|
||||
{
|
||||
private:
|
||||
template <size_t N>
|
||||
static void AppendImpl(Hasher &hasher, const std::tuple<Ts...> &value)
|
||||
{
|
||||
ChainedHasher<std::decay_t<decltype(std::get<N>(value))>>::Append(
|
||||
hasher, std::get<N>(value));
|
||||
if constexpr (N + 1 < sizeof...(Ts))
|
||||
{
|
||||
AppendImpl<N + 1>(hasher, value);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void Append(Hasher &hasher, const std::tuple<Ts...> &value)
|
||||
{
|
||||
if constexpr (sizeof...(Ts))
|
||||
{
|
||||
AppendImpl<0>(hasher, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::pair of hashable types.
|
||||
struct PairHasher
|
||||
{
|
||||
template <class T, class V>
|
||||
size_t operator()(const std::pair<T, V> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
ChainedHasher<std::pair<T, V>>::Append(hash, v);
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::array of a hashable type.
|
||||
struct ArrayHasher
|
||||
{
|
||||
template <class T, size_t N>
|
||||
size_t operator()(const std::array<T, N> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
ChainedHasher<std::array<T, N>>::Append(hash, v);
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::tuple of hashable types.
|
||||
struct TupleHasher
|
||||
{
|
||||
template <class T>
|
||||
size_t operator()(const T &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
ChainedHasher<T>::Append(hash, v);
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -20,9 +20,11 @@
|
||||
|
||||
#if defined(MFEM_USE_HIP) && defined(__HIP__)
|
||||
#define MFEM_USE_CUDA_OR_HIP
|
||||
constexpr bool mfem_use_gpu = true;
|
||||
#define MFEM_DEVICE __device__
|
||||
#define MFEM_HOST __host__
|
||||
#define MFEM_LAMBDA __host__ __device__
|
||||
#define MFEM_LAUNCH_BOUNDS __launch_bounds__
|
||||
// #define MFEM_HOST_DEVICE __host__ __device__ // defined in config/config.hpp
|
||||
#define MFEM_DEVICE_SYNC MFEM_GPU_CHECK(hipDeviceSynchronize())
|
||||
#define MFEM_STREAM_SYNC MFEM_GPU_CHECK(hipStreamSynchronize(0))
|
||||
|
||||
+19
-2
@@ -22,6 +22,14 @@
|
||||
//#define _WIN32
|
||||
//#define _aligned_malloc(s,a) malloc(s)
|
||||
|
||||
#ifdef NVTX_DEBUG_HPP
|
||||
#undef NVTX_COLOR
|
||||
#define NVTX_COLOR ::nvtx::kGold
|
||||
#include NVTX_DEBUG_HPP
|
||||
#else
|
||||
#define dbg(...)
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@@ -759,7 +767,14 @@ private:
|
||||
{
|
||||
switch (mt)
|
||||
{
|
||||
case MT::HOST_DEBUG: return new MmuHostMemorySpace();
|
||||
case MT::HOST_DEBUG:
|
||||
if (GetEnv("MFEM_MMU_STD"))
|
||||
{
|
||||
dbg("Using STD memory space for debug device!");
|
||||
return new StdHostMemorySpace();
|
||||
}
|
||||
dbg("Using MMU memory space for debug device!");
|
||||
return new MmuHostMemorySpace();
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
case MT::HOST_UMPIRE:
|
||||
return new UmpireHostMemorySpace(
|
||||
@@ -788,7 +803,9 @@ private:
|
||||
case MT::DEVICE_UMPIRE: return new NoDeviceMemorySpace();
|
||||
case MT::DEVICE_UMPIRE_2: return new NoDeviceMemorySpace();
|
||||
#endif
|
||||
case MT::DEVICE_DEBUG: return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE_DEBUG:
|
||||
if (GetEnv("MFEM_MMU_STD")) { return new StdDeviceMemorySpace(); }
|
||||
return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE:
|
||||
{
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
|
||||
@@ -200,6 +200,10 @@ void OptionsParser::Parse()
|
||||
isValid = isValidAsInt(argv[i]);
|
||||
*(int *)(options[j].var_ptr) = atoi(argv[i++]);
|
||||
break;
|
||||
case LONG:
|
||||
isValid = isValidAsInt(argv[i]);
|
||||
*(long long *)(options[j].var_ptr) = atoi(argv[i++]);
|
||||
break;
|
||||
case DOUBLE:
|
||||
isValid = isValidAsDouble(argv[i]);
|
||||
*(real_t *)(options[j].var_ptr) = atof(argv[i++]);
|
||||
@@ -278,6 +282,10 @@ void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
|
||||
case INT:
|
||||
os << *(int *)(opt.var_ptr);
|
||||
break;
|
||||
|
||||
case LONG:
|
||||
os << *(long long *)(opt.var_ptr);
|
||||
break;
|
||||
|
||||
case DOUBLE:
|
||||
os << *(real_t *)(opt.var_ptr);
|
||||
|
||||
@@ -31,7 +31,7 @@ class Vector;
|
||||
class OptionsParser
|
||||
{
|
||||
public:
|
||||
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR };
|
||||
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR , LONG };
|
||||
|
||||
private:
|
||||
struct Option
|
||||
@@ -98,6 +98,14 @@ public:
|
||||
required));
|
||||
}
|
||||
|
||||
/// Add a long integer option and set 'var' to receive the value.
|
||||
void AddOption(long long *var, const char *short_name, const char *long_name,
|
||||
const char *description, bool required = false)
|
||||
{
|
||||
options.Append(Option(LONG, var, short_name, long_name, description,
|
||||
required));
|
||||
}
|
||||
|
||||
/// Add a double option and set 'var' to receive the value.
|
||||
void AddOption(real_t *var, const char *short_name, const char *long_name,
|
||||
const char *description, bool required = false)
|
||||
|
||||
@@ -82,6 +82,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the DenseMatrix to reference the given sub-Memory of @a base.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the @a base Memory. */
|
||||
void MakeRef(Memory<real_t> &base, int offset, int h, int w)
|
||||
{
|
||||
data.MakeRef(base, offset, h*w);
|
||||
height = h; width = w;
|
||||
}
|
||||
|
||||
/// Change the data array and the size of the DenseMatrix.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the data array @a d. */
|
||||
|
||||
@@ -3634,12 +3634,25 @@ void HypreSmoother::SetType(HypreSmoother::Type type_, int relax_times_)
|
||||
relax_times = relax_times_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetType(HypreSmoother::Type &type_, int &relax_times_) const
|
||||
{
|
||||
type_ = static_cast<HypreSmoother::Type>(type);
|
||||
relax_times_ = relax_times;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetSOROptions(real_t relax_weight_, real_t omega_)
|
||||
{
|
||||
relax_weight = relax_weight_;
|
||||
omega = omega_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetSOROptions(real_t &relax_weight_, real_t &omega_) const
|
||||
{
|
||||
// TODO: are these used for all smoother types?
|
||||
relax_weight_ = relax_weight;
|
||||
omega_ = omega;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetPolyOptions(int poly_order_, real_t poly_fraction_,
|
||||
int eig_est_cg_iter_)
|
||||
{
|
||||
@@ -3648,6 +3661,15 @@ void HypreSmoother::SetPolyOptions(int poly_order_, real_t poly_fraction_,
|
||||
eig_est_cg_iter = eig_est_cg_iter_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetPolyOptions(int &poly_order_, real_t &poly_fraction_,
|
||||
int &eig_est_cg_iter_) const
|
||||
{
|
||||
// TODO: are these used for all smoother types?
|
||||
poly_order_ = poly_order;
|
||||
poly_fraction_ = poly_fraction;
|
||||
eig_est_cg_iter_ = eig_est_cg_iter;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetTaubinOptions(real_t lambda_, real_t mu_,
|
||||
int taubin_iter_)
|
||||
{
|
||||
@@ -3656,6 +3678,14 @@ void HypreSmoother::SetTaubinOptions(real_t lambda_, real_t mu_,
|
||||
taubin_iter = taubin_iter_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetTaubinOptions(real_t &lambda_, real_t &mu_,
|
||||
int &taubin_iter_) const
|
||||
{
|
||||
lambda_ = lambda;
|
||||
mu_ = mu;
|
||||
taubin_iter_ = taubin_iter;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetWindowByName(const char* name)
|
||||
{
|
||||
real_t a = -1, b, c;
|
||||
@@ -3678,6 +3708,13 @@ void HypreSmoother::SetWindowParameters(real_t a, real_t b, real_t c)
|
||||
window_params[2] = c;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetWindowParameters(real_t &a, real_t &b, real_t &c) const
|
||||
{
|
||||
a = window_params[0];
|
||||
b = window_params[1];
|
||||
c = window_params[2];
|
||||
}
|
||||
|
||||
void HypreSmoother::SetOperator(const Operator &op)
|
||||
{
|
||||
A = const_cast<HypreParMatrix *>(dynamic_cast<const HypreParMatrix *>(&op));
|
||||
@@ -4173,12 +4210,20 @@ HypreSolver::~HypreSolver()
|
||||
auxX.Delete();
|
||||
}
|
||||
|
||||
void HyprePCG::SetDefaultOptions()
|
||||
{
|
||||
// Explicitly set just in case past/future versions of hypre change the
|
||||
// defaults
|
||||
SetTol(1e-6);
|
||||
SetMaxIter(1000);
|
||||
}
|
||||
|
||||
HyprePCG::HyprePCG(MPI_Comm comm) : precond(NULL)
|
||||
{
|
||||
iterative_mode = true;
|
||||
|
||||
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
|
||||
SetDefaultOptions();
|
||||
}
|
||||
|
||||
HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
|
||||
@@ -4190,6 +4235,7 @@ HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
|
||||
HYPRE_ParCSRMatrixGetComm(*A, &comm);
|
||||
|
||||
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
|
||||
SetDefaultOptions();
|
||||
}
|
||||
|
||||
void HyprePCG::SetOperator(const Operator &op)
|
||||
@@ -4214,21 +4260,54 @@ void HyprePCG::SetOperator(const Operator &op)
|
||||
auxX.Delete(); auxX.Reset();
|
||||
}
|
||||
|
||||
void HyprePCG::SetUseTwoNorm(bool val)
|
||||
{
|
||||
HYPRE_PCGSetTwoNorm(pcg_solver, val);
|
||||
}
|
||||
|
||||
bool HyprePCG::GetUseTwoNorm() const
|
||||
{
|
||||
HYPRE_Int val;
|
||||
HYPRE_PCGGetTwoNorm(pcg_solver, &val);
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
void HyprePCG::SetTol(real_t tol)
|
||||
{
|
||||
HYPRE_PCGSetTol(pcg_solver, tol);
|
||||
}
|
||||
|
||||
real_t HyprePCG::GetTol() const
|
||||
{
|
||||
HYPRE_Real tol;
|
||||
HYPRE_PCGGetTol(pcg_solver, &tol);
|
||||
return tol;
|
||||
}
|
||||
|
||||
void HyprePCG::SetAbsTol(real_t atol)
|
||||
{
|
||||
HYPRE_PCGSetAbsoluteTol(pcg_solver, atol);
|
||||
}
|
||||
|
||||
real_t HyprePCG::GetAbsTol() const
|
||||
{
|
||||
HYPRE_Real atol;
|
||||
hypre_PCGGetAbsoluteTol(pcg_solver, &atol);
|
||||
return atol;
|
||||
}
|
||||
|
||||
void HyprePCG::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_PCGSetMaxIter(pcg_solver, max_iter);
|
||||
}
|
||||
|
||||
int HyprePCG::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_PCGGetMaxIter(pcg_solver, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HyprePCG::SetLogging(int logging)
|
||||
{
|
||||
HYPRE_PCGSetLogging(pcg_solver, logging);
|
||||
@@ -4344,6 +4423,20 @@ HyprePCG::~HyprePCG()
|
||||
HYPRE_ParCSRPCGDestroy(pcg_solver);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
HypreParVector HyprePCG::GetResiduals() const
|
||||
{
|
||||
HYPRE_ParVector r;
|
||||
HYPRE_ParCSRPCGGetResidual(pcg_solver, &r);
|
||||
return HypreParVector(r);
|
||||
}
|
||||
|
||||
void HyprePCG::GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p) const
|
||||
{
|
||||
auto r = GetResiduals();
|
||||
ParNormlp(r, p, r.GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
HypreGMRES::HypreGMRES(MPI_Comm comm) : precond(NULL)
|
||||
{
|
||||
@@ -4399,26 +4492,69 @@ void HypreGMRES::SetOperator(const Operator &op)
|
||||
auxX.Delete(); auxX.Reset();
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
HypreParVector HypreGMRES::GetResiduals() const
|
||||
{
|
||||
HYPRE_ParVector r;
|
||||
HYPRE_ParCSRGMRESGetResidual(gmres_solver, &r);
|
||||
return HypreParVector(r);
|
||||
}
|
||||
|
||||
void HypreGMRES::GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p) const
|
||||
{
|
||||
auto r = GetResiduals();
|
||||
ParNormlp(r, p, r.GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
void HypreGMRES::SetTol(real_t tol)
|
||||
{
|
||||
HYPRE_GMRESSetTol(gmres_solver, tol);
|
||||
}
|
||||
|
||||
real_t HypreGMRES::GetTol()const
|
||||
{
|
||||
HYPRE_Real tol;
|
||||
HYPRE_GMRESGetTol(gmres_solver, &tol);
|
||||
return tol;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetAbsTol(real_t tol)
|
||||
{
|
||||
HYPRE_GMRESSetAbsoluteTol(gmres_solver, tol);
|
||||
}
|
||||
|
||||
real_t HypreGMRES::GetAbsTol() const
|
||||
{
|
||||
HYPRE_Real atol;
|
||||
HYPRE_GMRESGetAbsoluteTol(gmres_solver, &atol);
|
||||
return atol;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_GMRESSetMaxIter(gmres_solver, max_iter);
|
||||
}
|
||||
|
||||
int HypreGMRES::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_GMRESGetMaxIter(gmres_solver, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetKDim(int k_dim)
|
||||
{
|
||||
HYPRE_GMRESSetKDim(gmres_solver, k_dim);
|
||||
}
|
||||
|
||||
int HypreGMRES::GetKDim() const
|
||||
{
|
||||
HYPRE_Int k_dim;
|
||||
HYPRE_GMRESGetKDim(gmres_solver, &k_dim);
|
||||
return k_dim;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetLogging(int logging)
|
||||
{
|
||||
HYPRE_GMRESSetLogging(gmres_solver, logging);
|
||||
@@ -4576,16 +4712,37 @@ void HypreFGMRES::SetTol(real_t tol)
|
||||
HYPRE_ParCSRFlexGMRESSetTol(fgmres_solver, tol);
|
||||
}
|
||||
|
||||
real_t HypreFGMRES::GetTol() const
|
||||
{
|
||||
HYPRE_Real tol;
|
||||
HYPRE_FlexGMRESGetTol(fgmres_solver, &tol);
|
||||
return tol;
|
||||
}
|
||||
|
||||
void HypreFGMRES::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESSetMaxIter(fgmres_solver, max_iter);
|
||||
}
|
||||
|
||||
int HypreFGMRES::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_FlexGMRESGetMaxIter(fgmres_solver, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HypreFGMRES::SetKDim(int k_dim)
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESSetKDim(fgmres_solver, k_dim);
|
||||
}
|
||||
|
||||
int HypreFGMRES::GetKDim() const
|
||||
{
|
||||
HYPRE_Int k_dim;
|
||||
HYPRE_FlexGMRESGetKDim(fgmres_solver, &k_dim);
|
||||
return k_dim;
|
||||
}
|
||||
|
||||
void HypreFGMRES::SetLogging(int logging)
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESSetLogging(fgmres_solver, logging);
|
||||
@@ -4682,6 +4839,21 @@ HypreFGMRES::~HypreFGMRES()
|
||||
HYPRE_ParCSRFlexGMRESDestroy(fgmres_solver);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
HypreParVector HypreFGMRES::GetResiduals() const
|
||||
{
|
||||
HYPRE_ParVector r;
|
||||
HYPRE_ParCSRFlexGMRESGetResidual(fgmres_solver, &r);
|
||||
return HypreParVector(r);
|
||||
}
|
||||
|
||||
void HypreFGMRES::GetFinalAbsResidualNorm(real_t &final_res_norm,
|
||||
real_t p) const
|
||||
{
|
||||
auto r = GetResiduals();
|
||||
ParNormlp(r, p, r.GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
void HypreDiagScale::SetOperator(const Operator &op)
|
||||
{
|
||||
@@ -5170,6 +5342,13 @@ void HypreBoomerAMG::ResetAMGPrecond()
|
||||
}
|
||||
}
|
||||
|
||||
int HypreBoomerAMG::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_BoomerAMGGetMaxIter(amg_precond, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HypreBoomerAMG::SetOperator(const Operator &op)
|
||||
{
|
||||
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
|
||||
|
||||
+97
-7
@@ -1160,6 +1160,15 @@ public:
|
||||
return HypreUsingGPU() ? l1Jacobi : l1GS;
|
||||
}
|
||||
|
||||
/// Default solver settings:
|
||||
/// type = DefaultType()
|
||||
/// relax_times = 1
|
||||
/// omega = 1.0
|
||||
/// poly_order = 2
|
||||
/// poly_fraction = 0.3
|
||||
/// lambda = 0.5
|
||||
/// mu = -0.5
|
||||
/// taubin_iter = 40
|
||||
HypreSmoother();
|
||||
|
||||
HypreSmoother(const HypreParMatrix &A_, int type = DefaultType(),
|
||||
@@ -1169,20 +1178,28 @@ public:
|
||||
|
||||
/// Set the relaxation type and number of sweeps
|
||||
void SetType(HypreSmoother::Type type, int relax_times = 1);
|
||||
using Operator::GetType;
|
||||
void GetType(HypreSmoother::Type &type, int &relax_times) const;
|
||||
/// Set SOR-related parameters
|
||||
void SetSOROptions(real_t relax_weight, real_t omega);
|
||||
void GetSOROptions(real_t &relax_weight, real_t &omega) const;
|
||||
|
||||
/// 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, real_t poly_fraction,
|
||||
int eig_est_cg_iter = 10);
|
||||
void GetPolyOptions(int &poly_order, real_t &poly_fraction,
|
||||
int &eig_est_cg_iter) const;
|
||||
/// Set parameters for Taubin's lambda-mu method
|
||||
void SetTaubinOptions(real_t lambda, real_t mu, int iter);
|
||||
void GetTaubinOptions(real_t &lambda, real_t &mu, int &iter) const;
|
||||
|
||||
/// Convenience function for setting canonical windowing parameters
|
||||
void SetWindowByName(const char* window_name);
|
||||
/// Set parameters for windowing function for FIR smoother.
|
||||
void SetWindowParameters(real_t a, real_t b, real_t c);
|
||||
void GetWindowParameters(real_t &a, real_t &b, real_t &c) const;
|
||||
/// Compute window and Chebyshev coefficients for given polynomial order.
|
||||
void SetFIRCoefficients(real_t max_eig);
|
||||
|
||||
@@ -1190,12 +1207,15 @@ public:
|
||||
/** By default, the l1-norms take their sign from the corresponding diagonal
|
||||
entries in the associated matrix. */
|
||||
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
|
||||
bool IsPositiveDiagonal() const { return pos_l1_norms; };
|
||||
|
||||
/** Explicitly indicate whether the linear system matrix A is symmetric. If A
|
||||
is symmetric, the smoother will also be symmetric. In this case, calling
|
||||
MultTranspose will be redirected to Mult. (This is also done if the
|
||||
smoother is diagonal.) By default, A is assumed to be nonsymmetric. */
|
||||
void SetOperatorSymmetry(bool is_sym) { A_is_symmetric = is_sym; }
|
||||
/// @return true if the smoother assumes A is symmetric, false otherwise
|
||||
bool IsOperatorSymmetric() const { return A_is_symmetric; }
|
||||
|
||||
/** Set/update the associated operator. Must be called after setting the
|
||||
HypreSmoother type and options. */
|
||||
@@ -1327,6 +1347,7 @@ public:
|
||||
#endif
|
||||
|
||||
/// PCG solver in hypre
|
||||
/// Defaults to (relative) tol=1e-6, atol=0, max_iter=1000
|
||||
class HyprePCG : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1334,6 +1355,9 @@ private:
|
||||
|
||||
HypreSolver * precond;
|
||||
|
||||
/// Default PCG options
|
||||
void SetDefaultOptions();
|
||||
|
||||
public:
|
||||
HyprePCG(MPI_Comm comm);
|
||||
|
||||
@@ -1342,8 +1366,11 @@ public:
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void SetTol(real_t tol);
|
||||
real_t GetTol() const;
|
||||
void SetAbsTol(real_t atol);
|
||||
real_t GetAbsTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
int GetMaxIter() const;
|
||||
void SetLogging(int logging);
|
||||
void SetPrintLevel(int print_lvl);
|
||||
|
||||
@@ -1368,12 +1395,32 @@ public:
|
||||
num_iterations = internal::to_int(num_it);
|
||||
}
|
||||
|
||||
/// Gets the relative residual norm
|
||||
void GetFinalResidualNorm(real_t &final_res_norm) const
|
||||
{
|
||||
HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(pcg_solver,
|
||||
&final_res_norm);
|
||||
}
|
||||
|
||||
/// @param[in] use
|
||||
/// Convergence criterion:
|
||||
/// - when true: (r, r) < max(r_tol^2 (b, b), a_tol^2)
|
||||
/// - when false: (r, A r) < max(r_tol^2 (b, A b), a_tol^2)
|
||||
/// @sa HYPRE_PCGSetTwoNorm
|
||||
void SetUseTwoNorm(bool use);
|
||||
|
||||
/// @sa HYPRE_PCGGetTwoNorm
|
||||
bool GetUseTwoNorm() const;
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
/// Gets the internal Hypre solver residual vector.
|
||||
/// @sa HYPRE_ParCSRPCGGetResidual
|
||||
HypreParVector GetResiduals() const;
|
||||
|
||||
/// Computes the absolute residual p-norm.
|
||||
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
|
||||
#endif
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal pcg_solver
|
||||
operator HYPRE_Solver() const override { return pcg_solver; }
|
||||
|
||||
@@ -1391,7 +1438,8 @@ public:
|
||||
virtual ~HyprePCG();
|
||||
};
|
||||
|
||||
/// GMRES solver in hypre
|
||||
/// GMRES solver in hypre.
|
||||
/// Defaults to k=50, (relative) tol=1e-6, atol=0, max_iter=100.
|
||||
class HypreGMRES : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1410,9 +1458,13 @@ public:
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void SetTol(real_t tol);
|
||||
real_t GetTol() const;
|
||||
void SetAbsTol(real_t tol);
|
||||
real_t GetAbsTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
int GetMaxIter() const;
|
||||
void SetKDim(int dim);
|
||||
int GetKDim() const;
|
||||
void SetLogging(int logging);
|
||||
void SetPrintLevel(int print_lvl);
|
||||
|
||||
@@ -1432,12 +1484,22 @@ public:
|
||||
num_iterations = internal::to_int(num_it);
|
||||
}
|
||||
|
||||
/// Gets the relative residual norm
|
||||
void GetFinalResidualNorm(real_t &final_res_norm) const
|
||||
{
|
||||
HYPRE_ParCSRGMRESGetFinalRelativeResidualNorm(gmres_solver,
|
||||
&final_res_norm);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
/// Gets the internal Hypre solver residual vector.
|
||||
/// @sa HYPRE_ParCSRGMRESGetResidual
|
||||
HypreParVector GetResiduals() const;
|
||||
|
||||
/// Computes the absolute residual p-norm.
|
||||
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
|
||||
#endif
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal gmres_solver
|
||||
operator HYPRE_Solver() const override { return gmres_solver; }
|
||||
|
||||
@@ -1455,7 +1517,8 @@ public:
|
||||
virtual ~HypreGMRES();
|
||||
};
|
||||
|
||||
/// Flexible GMRES solver in hypre
|
||||
/// Flexible GMRES solver in hypre.
|
||||
/// Defaults to k=50, (relative) tol=1e-6, max_iter=100.
|
||||
class HypreFGMRES : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1474,8 +1537,11 @@ public:
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void SetTol(real_t tol);
|
||||
real_t GetTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
int GetMaxIter() const;
|
||||
void SetKDim(int dim);
|
||||
int GetKDim() const;
|
||||
void SetLogging(int logging);
|
||||
void SetPrintLevel(int print_lvl);
|
||||
|
||||
@@ -1495,12 +1561,22 @@ public:
|
||||
num_iterations = internal::to_int(num_it);
|
||||
}
|
||||
|
||||
/// Gets the relative residual norm
|
||||
void GetFinalResidualNorm(real_t &final_res_norm) const
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESGetFinalRelativeResidualNorm(fgmres_solver,
|
||||
&final_res_norm);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
/// Gets the internal Hypre solver residual vector.
|
||||
/// @sa HYPRE_ParCSRFlexGMRESGetResidual
|
||||
HypreParVector GetResiduals() const;
|
||||
|
||||
/// Computes the absolute residual p-norm.
|
||||
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
|
||||
#endif
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal fgmres_solver
|
||||
operator HYPRE_Solver() const override { return fgmres_solver; }
|
||||
|
||||
@@ -1556,7 +1632,8 @@ public:
|
||||
virtual ~HypreDiagScale() { }
|
||||
};
|
||||
|
||||
/// The ParaSails preconditioner in hypre
|
||||
/// The ParaSails preconditioner in hypre.
|
||||
/// See SetDefaultOptions() for default solver options.
|
||||
class HypreParaSails : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1685,10 +1762,14 @@ public:
|
||||
/**
|
||||
@brief Wrapper for Hypre's native parallel ILU preconditioner.
|
||||
|
||||
The default ILU factorization type is ILU(k). If you need to change this, or
|
||||
any other option, you can use the HYPRE_Solver method to cast the object for use
|
||||
with Hypre's native functions. For example, if want to use natural ordering
|
||||
rather than RCM reordering, you can use the following approach:
|
||||
Default parameters: ILU(k) factorization type, tol=0.0 (for use as a
|
||||
preconditioner), fill level = 1 (for ILU(k)), reverse Cuthill-McKee (RCM)
|
||||
re-ordering.
|
||||
|
||||
If you need to change this, or any other option, you can use the HYPRE_Solver
|
||||
method to cast the object for use with Hypre's native functions. For example, if
|
||||
want to use natural ordering rather than RCM reordering, you can use the
|
||||
following approach:
|
||||
|
||||
@code
|
||||
mfem::HypreILU ilu();
|
||||
@@ -1829,6 +1910,7 @@ public:
|
||||
|
||||
void SetMaxIter(int max_iter)
|
||||
{ HYPRE_BoomerAMGSetMaxIter(amg_precond, max_iter); }
|
||||
int GetMaxIter() const;
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetMaxLevels(int max_levels)
|
||||
@@ -1853,6 +1935,8 @@ public:
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetRelaxType(int relax_type)
|
||||
{ HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type); }
|
||||
// not implemented in hypre
|
||||
// int GetRelaxType() const;
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetCycleType(int cycle_type)
|
||||
@@ -2153,8 +2237,14 @@ public:
|
||||
~HypreLOBPCG();
|
||||
|
||||
void SetTol(real_t tol);
|
||||
// not implemented in HYPRE
|
||||
// real_t GetTol() const;
|
||||
void SetRelTol(real_t rel_tol);
|
||||
// not implemented in HYPRE
|
||||
// real_t GetRelTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
// not implemented in HYPRE
|
||||
// int GetMaxIter() const;
|
||||
void SetPrintLevel(int logging);
|
||||
void SetNumModes(int num_eigs) { nev = num_eigs; }
|
||||
void SetPrecondUsageMode(int pcg_mode);
|
||||
|
||||
@@ -3639,12 +3639,20 @@ void PetscBDDCSolver::BDDCSolverConstructor(const PetscBDDCSolverParams &opts)
|
||||
// make sure ess/nat_dof have been collectively set
|
||||
PetscBool lpr = PETSC_FALSE,pr;
|
||||
if (opts.ess_dof) { lpr = PETSC_TRUE; }
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
|
||||
#else
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPI_C_BOOL,MPI_LOR,comm);
|
||||
#endif
|
||||
CCHKERRQ(comm,mpiierr);
|
||||
MFEM_VERIFY(lpr == pr,"ess_dof should be collectively set");
|
||||
lpr = PETSC_FALSE;
|
||||
if (opts.nat_dof) { lpr = PETSC_TRUE; }
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
|
||||
#else
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPI_C_BOOL,MPI_LOR,comm);
|
||||
#endif
|
||||
CCHKERRQ(comm,mpiierr);
|
||||
MFEM_VERIFY(lpr == pr,"nat_dof should be collectively set");
|
||||
// make sure fields have been collectively set
|
||||
@@ -4058,8 +4066,13 @@ void PetscNonlinearSolver::SetOperator(const Operator &op)
|
||||
ls = (PetscBool)(height == op.Height() && width == op.Width() &&
|
||||
(void*)&op == fctx &&
|
||||
(void*)&op == jctx);
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
mpiierr = MPI_Allreduce(&ls,&gs,1,MPIU_BOOL,MPI_LAND,
|
||||
PetscObjectComm((PetscObject)snes));
|
||||
#else
|
||||
mpiierr = MPI_Allreduce(&ls,&gs,1,MPI_C_BOOL,MPI_LAND,
|
||||
PetscObjectComm((PetscObject)snes));
|
||||
#endif
|
||||
CCHKERRQ(PetscObjectComm((PetscObject)snes),mpiierr);
|
||||
if (!gs)
|
||||
{
|
||||
|
||||
@@ -1066,6 +1066,11 @@ void SparseMatrix::BooleanMultTranspose(const Array<int> &x,
|
||||
y.SetSize(Width());
|
||||
y = 0;
|
||||
|
||||
HostReadI();
|
||||
HostReadJ();
|
||||
x.HostRead();
|
||||
y.HostReadWrite();
|
||||
|
||||
for (int i = 0; i < Height(); i++)
|
||||
{
|
||||
if (x[i])
|
||||
|
||||
+12
-1
@@ -363,14 +363,19 @@ void SuperLUSolver::Init(MPI_Comm comm)
|
||||
// Set default options:
|
||||
// options.Fact = DOFACT;
|
||||
// options.Equil = YES;
|
||||
// options.ParSymbFact = NO;
|
||||
// options.ColPerm = METIS_AT_PLUS_A;
|
||||
// options.RowPerm = LargeDiag_MC64;
|
||||
// options.ReplaceTinyPivot = NO;
|
||||
// options.Trans = NOTRANS;
|
||||
// options.IterRefine = SLU_DOUBLE;
|
||||
// options.Trans = NOTRANS;
|
||||
// options.SolveInitialized = NO;
|
||||
// options.RefineInitialized = NO;
|
||||
// options.PrintStat = YES;
|
||||
// options.lookahead_etree = NO;
|
||||
// options.num_lookaheads = 10;
|
||||
// options.superlu_acc_offload = 1;
|
||||
// options.SymPattern = NO;
|
||||
superlu_dist_options_t *options = (superlu_dist_options_t *)optionsPtr_;
|
||||
set_default_options_dist(options);
|
||||
#if SUPERLU_DIST_MAJOR_VERSION > 7 || \
|
||||
@@ -472,6 +477,12 @@ void SuperLUSolver::SetFact(superlu::Fact fact)
|
||||
options->Fact = opt;
|
||||
}
|
||||
|
||||
void SuperLUSolver::SetDeviceOffload(bool offload)
|
||||
{
|
||||
superlu_dist_options_t *options = (superlu_dist_options_t *)optionsPtr_;
|
||||
options->superlu_acc_offload = offload;
|
||||
}
|
||||
|
||||
void SuperLUSolver::SetOperator(const Operator &op)
|
||||
{
|
||||
// Verify that we have a compatible operator
|
||||
|
||||
+6
-1
@@ -250,7 +250,8 @@ public:
|
||||
work (default false) */
|
||||
void SetSymmetricPattern(bool sym);
|
||||
|
||||
/** @brief Specify whether to perform parallel symbolic factorization.
|
||||
/** @brief Specify whether to perform parallel symbolic factorization
|
||||
(default false)
|
||||
@note If true SuperLU will use superlu::PARMETIS for the Column
|
||||
Permutation regardless of the setting */
|
||||
void SetParSymbFact(bool par);
|
||||
@@ -263,6 +264,10 @@ public:
|
||||
superlu::FACTORED*/
|
||||
void SetFact(superlu::Fact fact);
|
||||
|
||||
/** @brief Specify whether to offload numerical factorization onto the device
|
||||
(default true if SuperLU_DIST has been compiled with GPU support) */
|
||||
void SetDeviceOffload(bool offload);
|
||||
|
||||
// Processor grid for SuperLU_DIST.
|
||||
const int nprow_, npcol_, npdep_;
|
||||
|
||||
|
||||
@@ -794,7 +794,6 @@ status info:
|
||||
$(info MFEM_MPI_NP = $(MFEM_MPI_NP))
|
||||
@true
|
||||
|
||||
ASTYLE_BIN = astyle
|
||||
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
|
||||
ASTYLE_VER = "Artistic Style Version 3.1"
|
||||
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
|
||||
|
||||
+58
-52
@@ -493,8 +493,7 @@ void Mesh::GetBdrElementTransformation(int i,
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
int idx = vdofs[n*k+j];
|
||||
pm(k,j) = nodes((idx<0)? -1-idx:idx);
|
||||
pm(k,j) = nodes(UnsignIndex(vdofs[n*k+j]));
|
||||
}
|
||||
}
|
||||
ElTr->SetFE(bdr_el);
|
||||
@@ -1356,7 +1355,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[0].conformity = ElementConformity::Coincident;
|
||||
face.element[1].conformity = ElementConformity::Coincident;
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].orientation = inf2%64;
|
||||
}
|
||||
}
|
||||
@@ -1379,7 +1378,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[0].conformity = ElementConformity::Coincident;
|
||||
face.element[1].conformity = ElementConformity::Superset;
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].orientation = inf2%64;
|
||||
}
|
||||
face.point_matrix = nc_faces_info[ncface].PointMatrix;
|
||||
@@ -1405,7 +1404,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[0].conformity = ElementConformity::Superset;
|
||||
face.element[1].conformity = ElementConformity::Coincident;
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].orientation = inf2%64;
|
||||
face.point_matrix = nc_faces_info[ncface].PointMatrix;
|
||||
}
|
||||
@@ -1438,7 +1437,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::SharedConforming:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -1448,7 +1447,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::SharedSlaveNonconforming:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -1456,7 +1455,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::GhostSlave:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -6514,7 +6513,7 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
|
||||
input >> edge_to_ukv[j] >> v[0] >> v[1];
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
edge_to_ukv[j] = -1 - edge_to_ukv[j];
|
||||
edge_to_ukv[j] = FlipIndexSign(edge_to_ukv[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6551,9 +6550,6 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
const int NP = NumOfElements; // number of patches
|
||||
const int NPKV = NP * dim; // number of patch knotvectors
|
||||
constexpr int notset = -9999999;
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
auto unSign = [](int i) { return (i < 0) ? -1 - i : i; };
|
||||
// Local edge index -> dimension convention
|
||||
auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
|
||||
|
||||
@@ -6569,7 +6565,7 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
{
|
||||
GetElementVertices(i, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : flipSign(i);
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : FlipIndexSign(i);
|
||||
ukv_to_rpkv[i] = i;
|
||||
}
|
||||
return;
|
||||
@@ -6619,14 +6615,14 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
// We've set this edge already - link this index to it
|
||||
if (edge_to_pkv[edge] != notset)
|
||||
{
|
||||
const int pkv_other = unSign(edge_to_pkv[edge]);
|
||||
const int pkv_other = UnsignIndex(edge_to_pkv[edge]);
|
||||
unite(pkv, pkv_other);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEdgeVertices(edge, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : flipSign(pkv);
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : FlipIndexSign(pkv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6653,10 +6649,10 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
edge_to_ukv.SetSize(NumOfEdges);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
const int pkv = unSign(edge_to_pkv[i]);
|
||||
const int pkv = UnsignIndex(edge_to_pkv[i]);
|
||||
const int rpkv = pkv_to_rpkv[pkv];
|
||||
const int ukv = rpkv_to_ukv[rpkv];
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? flipSign(ukv) : ukv;
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? FlipIndexSign(ukv) : ukv;
|
||||
}
|
||||
|
||||
CorrectPatchTopoOrientations(edge_to_ukv);
|
||||
@@ -6667,9 +6663,6 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
const int dim = Dimension(); // Topological (not physical) dimension
|
||||
if (dim == 1) { return; }
|
||||
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
|
||||
const Table *face2elem = GetFaceToElementTable();
|
||||
Array<int> pfaces, orient;
|
||||
Array<int> fe, feo;
|
||||
@@ -6688,7 +6681,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (auto e : fe)
|
||||
{
|
||||
const int skv = edge_to_ukv[e];
|
||||
if (skv == kv || flipSign(skv) == kv) { hasKV = true; }
|
||||
if (skv == kv || FlipIndexSign(skv) == kv) { hasKV = true; }
|
||||
}
|
||||
if (hasKV)
|
||||
{
|
||||
@@ -6718,7 +6711,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
};
|
||||
}
|
||||
|
||||
Array<int> ukvs((dim==2) ? 4 : 12);
|
||||
Array<int> ukvs((dim == 2) ? 4 : 12);
|
||||
Array<int> pe, oe;
|
||||
bool initKV = false;
|
||||
|
||||
@@ -6732,7 +6725,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (int i = 0; i < pe.Size(); i++)
|
||||
{
|
||||
ukvs[i] = edge_to_ukv[pe[i]];
|
||||
ukvs[i] = (oe[i] < 0) ? flipSign(ukvs[i]) : ukvs[i];
|
||||
ukvs[i] = (oe[i] < 0) ? FlipIndexSign(ukvs[i]) : ukvs[i];
|
||||
}
|
||||
|
||||
// Find the direction with this kv.
|
||||
@@ -6740,12 +6733,19 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (int d=0; d<dim; ++d) // Loop over directions.
|
||||
{
|
||||
const int skv = edge_to_ukv[pe[dir_edges[d][0]]];
|
||||
if (skv == kv || flipSign(skv) == kv)
|
||||
if (skv == kv || FlipIndexSign(skv) == kv)
|
||||
{
|
||||
thisDir = d;
|
||||
for (auto e : dir_edges[d])
|
||||
if (!edgeSet[pe[e]])
|
||||
{
|
||||
thisDir = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(thisDir >= 0, "");
|
||||
if (thisDir == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For this direction, find any edge already set. If no edge is set, we
|
||||
// arbitrarily take the first.
|
||||
@@ -6777,12 +6777,12 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
}
|
||||
|
||||
const int edge = pe[i];
|
||||
if ((dim == 2 && ukvs[i] != flipSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == flipSign(ukvs[ref_edge0])))
|
||||
if ((dim == 2 && ukvs[i] != FlipIndexSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == FlipIndexSign(ukvs[ref_edge0])))
|
||||
{
|
||||
// Flip the sign of this edge
|
||||
MFEM_VERIFY(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = flipSign(edge_to_ukv[edge]);
|
||||
MFEM_ASSERT(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = FlipIndexSign(edge_to_ukv[edge]);
|
||||
}
|
||||
|
||||
edgeSet[edge] = true;
|
||||
@@ -6827,10 +6827,11 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
int unsetDim = -1;
|
||||
for (int d=0; d<dim; ++d) // Loop over dimensions.
|
||||
{
|
||||
if (!edgeSet[pe[dir_edges[d][0]]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
for (auto e : dir_edges[d])
|
||||
if (!edgeSet[pe[e]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (unsetDim == -1)
|
||||
@@ -6839,9 +6840,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
continue;
|
||||
}
|
||||
|
||||
const int kv_signed = edge_to_ukv[pe[dir_edges[unsetDim][0]]];
|
||||
const int kv = kv_signed < 0 ? flipSign(kv_signed) : kv_signed;
|
||||
MFEM_VERIFY(!edgeSet[pe[dir_edges[unsetDim][0]]], "");
|
||||
const int kv = UnsignIndex(edge_to_ukv[pe[dir_edges[unsetDim][0]]]);
|
||||
|
||||
initKV = false;
|
||||
|
||||
@@ -6891,6 +6890,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
bool allSet = true;
|
||||
for (auto eset : edgeSet)
|
||||
{
|
||||
@@ -6899,7 +6899,8 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
allSet = false;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(allSet && unset.size() == 0, "Some edge is not set");
|
||||
MFEM_ASSERT(allSet && unset.size() == 0, "Some edge is not set");
|
||||
#endif
|
||||
|
||||
delete face2elem;
|
||||
}
|
||||
@@ -6941,7 +6942,7 @@ void Mesh::LoadNonconformingPatchTopo(std::istream &input,
|
||||
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
ukv = -1 - ukv;
|
||||
ukv = FlipIndexSign(ukv);
|
||||
}
|
||||
edge_to_ukv[j] = ukv;
|
||||
}
|
||||
@@ -12438,11 +12439,7 @@ void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
edge_vertex->GetRow(i, vert);
|
||||
int ki = e_to_k[i];
|
||||
if (ki < 0)
|
||||
{
|
||||
ki = -1 - ki;
|
||||
}
|
||||
const int ki = UnsignIndex(e_to_k[i]);
|
||||
|
||||
if (vmap)
|
||||
{
|
||||
@@ -15751,9 +15748,18 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
{
|
||||
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
|
||||
|
||||
auto factor = [&](int N)
|
||||
// Closest int divisor to the cubit root, going down.
|
||||
auto factor3 = [](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
|
||||
for (int i = static_cast<int>(round(cbrt(N))); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
|
||||
// Closest int divisor to the square root, going down.
|
||||
auto factor2 = [](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(round(sqrt(N))); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
@@ -15777,22 +15783,22 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
int el0_x, el0_y, el0_z;
|
||||
if (dim == 2)
|
||||
{
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_x = factor2(mpi_cnt);
|
||||
mpi_y = mpi_cnt / mpi_x;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_y = factor(el0);
|
||||
el0_y = factor2(el0);
|
||||
el0_x = el0 / el0_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_y = factor(mpi_cnt / mpi_x);
|
||||
mpi_x = factor3(mpi_cnt);
|
||||
mpi_y = factor2(mpi_cnt / mpi_x);
|
||||
mpi_z = mpi_cnt / mpi_x / mpi_y;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_z = factor(el0);
|
||||
el0_y = factor(el0 / el0_z);
|
||||
el0_z = factor3(el0);
|
||||
el0_y = factor2(el0 / el0_z);
|
||||
el0_x = el0 / el0_y / el0_z;
|
||||
}
|
||||
|
||||
|
||||
+15
-10
@@ -2078,12 +2078,13 @@ public:
|
||||
contrary to the ones obtained through Mesh::GetFacesElements and can
|
||||
directly be used, e.g., Elem1 and Elem2 indices.
|
||||
Likewise the orientations for Elem1 and Elem2 already take into account
|
||||
special cases and can be used as is.
|
||||
*/
|
||||
special cases and can be used as is. */
|
||||
struct FaceInformation
|
||||
{
|
||||
/// The face topology (boundary, conforming, or nonconforming).
|
||||
FaceTopology topology;
|
||||
|
||||
/// Information about the adjacent elements.
|
||||
struct
|
||||
{
|
||||
ElementLocation location;
|
||||
@@ -2093,8 +2094,13 @@ public:
|
||||
int orientation;
|
||||
} element[2];
|
||||
|
||||
/// Detailed face information (see FaceInfoTag).
|
||||
FaceInfoTag tag;
|
||||
|
||||
/// If the face is nonconforming, the index of the NC face. -1 otherwise.
|
||||
int ncface;
|
||||
|
||||
/// The point matrix for nonconforming faces.
|
||||
const DenseMatrix* point_matrix;
|
||||
|
||||
/** @brief Return true if the face is a local interior face which is NOT
|
||||
@@ -2113,21 +2119,20 @@ public:
|
||||
|
||||
/** @brief return true if the face is an interior face to the computation
|
||||
domain, either a local or shared interior face (not a boundary face)
|
||||
which is NOT a master nonconforming face.
|
||||
*/
|
||||
which is NOT a master nonconforming face. */
|
||||
bool IsInterior() const
|
||||
{
|
||||
return topology == FaceTopology::Conforming ||
|
||||
topology == FaceTopology::Nonconforming;
|
||||
}
|
||||
|
||||
/** @brief Return true if the face is a boundary face. */
|
||||
/// Return true if the face is a boundary face.
|
||||
bool IsBoundary() const
|
||||
{
|
||||
return topology == FaceTopology::Boundary;
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is of the same type as @a type.
|
||||
/// Return true if the face is of the same type as @a type.
|
||||
bool IsOfFaceType(FaceType type) const
|
||||
{
|
||||
switch (type)
|
||||
@@ -2141,13 +2146,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is a conforming face.
|
||||
/// Return true if the face is a conforming face.
|
||||
bool IsConforming() const
|
||||
{
|
||||
return topology == FaceTopology::Conforming;
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is a nonconforming fine face.
|
||||
/// Return true if the face is a nonconforming fine face.
|
||||
bool IsNonconformingFine() const
|
||||
{
|
||||
return topology == FaceTopology::Nonconforming &&
|
||||
@@ -2155,7 +2160,7 @@ public:
|
||||
element[1].conformity == ElementConformity::Superset);
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is a nonconforming coarse face.
|
||||
/// Return true if the face is a nonconforming coarse face.
|
||||
/** Note that ghost nonconforming master faces cannot be clearly
|
||||
identified as such with the currently available information, so this
|
||||
method will return false for such faces. */
|
||||
@@ -2165,7 +2170,7 @@ public:
|
||||
element[1].conformity == ElementConformity::Subset;
|
||||
}
|
||||
|
||||
/// @brief cast operator from FaceInformation to FaceInfo.
|
||||
/// cast operator from FaceInformation to FaceInfo.
|
||||
operator Mesh::FaceInfo() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ int ThresholdRefiner::ApplyImpl(Mesh &mesh)
|
||||
if (num_marked_elements == 0LL) { return STOP; }
|
||||
|
||||
mesh.GeneralRefinement(marked_elements, non_conforming, nc_limit);
|
||||
return CONTINUE + REFINED;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
|
||||
}
|
||||
|
||||
void ThresholdRefiner::Reset()
|
||||
@@ -162,7 +162,7 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh)
|
||||
const Vector &local_err = estimator.GetLocalErrors();
|
||||
bool derefs = mesh.DerefineByError(local_err, threshold, nc_limit, op);
|
||||
|
||||
return derefs ? CONTINUE + DEREFINED : NONE;
|
||||
return derefs ? static_cast<int>(CONTINUE) + static_cast<int>(DEREFINED) : NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it)
|
||||
}
|
||||
delete l2fes;
|
||||
delete gf;
|
||||
return CONTINUE + REFINED;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
|
||||
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ int Rebalancer::ApplyImpl(Mesh &mesh)
|
||||
if (pmesh && pmesh->Nonconforming())
|
||||
{
|
||||
pmesh->Rebalance();
|
||||
return CONTINUE + REBALANCED;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REBALANCED);
|
||||
}
|
||||
#endif
|
||||
return NONE;
|
||||
|
||||
+2
-2
@@ -3542,7 +3542,7 @@ void NCMesh::TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
|
||||
|
||||
// create a slave face record with a degenerate point matrix
|
||||
face_list.slaves.Append(
|
||||
Slave(-1 - enode.edge_index,
|
||||
Slave(FlipIndexSign(enode.edge_index),
|
||||
eid[0].element, eid[0].local, Geometry::SQUARE));
|
||||
Slave &sl = face_list.slaves.Last();
|
||||
|
||||
@@ -3589,7 +3589,7 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
|
||||
// non-slave edge is really a (face-)slave itself.
|
||||
const MeshId &eid = *eid_and_type.id;
|
||||
face_list.slaves.Append(
|
||||
Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
|
||||
Slave(FlipIndexSign(eid.index), eid.element, eid.local, Geometry::TRIANGLE));
|
||||
|
||||
int v0index = nodes[vn0].vert_index;
|
||||
int v1index = nodes[vn1].vert_index;
|
||||
|
||||
+40
-44
@@ -93,7 +93,7 @@ void NCNURBSExtension::GetMasterEdgeEntities(
|
||||
}
|
||||
else
|
||||
{
|
||||
const int auxEdge = -1 - edge_i;
|
||||
const int auxEdge = FlipIndexSign(edge_i);
|
||||
GetAuxEdgeVertices(auxEdge, sverts);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
{
|
||||
if (edge < 0)
|
||||
{
|
||||
sideAuxEdges[s].Append(-1 - edge);
|
||||
sideAuxEdges[s].Append(FlipIndexSign(edge));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,7 +456,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
== afverts[j], "");
|
||||
}
|
||||
|
||||
ori_f2 = -1 - ori_f2;
|
||||
ori_f2 = FlipIndexSign(ori_f2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -468,7 +468,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
}
|
||||
|
||||
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
|
||||
SlaveFaceInfo{-1 - afid, ori_f2,
|
||||
SlaveFaceInfo{FlipIndexSign(afid), ori_f2,
|
||||
{fki(vMinID,0), fki(vMinID,1)},
|
||||
{
|
||||
fki((vMinID + 2) % 4,0) - fki(vMinID,0),
|
||||
@@ -509,7 +509,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
auxFaces.push_back(auxFace);
|
||||
|
||||
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
|
||||
SlaveFaceInfo{-1 - auxFaceId, ori_f,
|
||||
SlaveFaceInfo{FlipIndexSign(auxFaceId), ori_f,
|
||||
{fki(vMinID,0), fki(vMinID,1)},
|
||||
{
|
||||
fki((vMinID + 2) % 4,0) - fki(vMinID,0),
|
||||
@@ -622,7 +622,7 @@ void NCNURBSExtension::GetAuxFaceEdges(int auxFace, Array<int> &edges) const
|
||||
}
|
||||
else // Auxiliary edge
|
||||
{
|
||||
edges[i] = -1 - auxv2e.at(edge_v);
|
||||
edges[i] = FlipIndexSign(auxv2e.at(edge_v));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,7 +633,7 @@ int OffsetHelper(int i, int j, const Array<int> &a, const Array<int> &b)
|
||||
{
|
||||
if (i < 0)
|
||||
{
|
||||
return b[-1 - i + j];
|
||||
return b[FlipIndexSign(i) + j];
|
||||
}
|
||||
else if (i + j < a.Size())
|
||||
{
|
||||
@@ -679,7 +679,7 @@ void NCNURBSExtension::GetMasterEdgeDofs(bool dof, int me,
|
||||
}
|
||||
else // Auxiliary edge
|
||||
{
|
||||
GetAuxEdgeVertices(-1 - slaveId, svert);
|
||||
GetAuxEdgeVertices(FlipIndexSign(slaveId), svert);
|
||||
}
|
||||
|
||||
bool reverse = false;
|
||||
@@ -872,7 +872,7 @@ void ReorderArray2D(int i0, int j0, const Array2D<int> &a,
|
||||
// Set a quadrilateral vertex index permutation for a given orientation.
|
||||
void GetVertexOrdering(int ori, std::array<int, 4> &perm)
|
||||
{
|
||||
const int oriAbs = ori < 0 ? -1 - ori : ori;
|
||||
const int oriAbs = UnsignIndex(ori);
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
@@ -1094,7 +1094,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
if (slaveId < 0)
|
||||
{
|
||||
// Auxiliary face
|
||||
const int auxFace = -1 - slaveId;
|
||||
const int auxFace = FlipIndexSign(slaveId);
|
||||
|
||||
// Set slave face entity dimensions.
|
||||
if (dof)
|
||||
@@ -1171,7 +1171,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
}
|
||||
else
|
||||
{
|
||||
const int auxEdge = -1 - edge;
|
||||
const int auxEdge = FlipIndexSign(edge);
|
||||
GetAuxEdgeVertices(auxEdge, evert);
|
||||
}
|
||||
MFEM_ASSERT(evert[0] == vstart || evert[1] == vstart, "");
|
||||
@@ -1184,7 +1184,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
// dimensions of the master face, by using ori.
|
||||
int e1 = -1, e2 = -1;
|
||||
{
|
||||
const int aori = ori < 0 ? -1 - ori : ori;
|
||||
const int aori = UnsignIndex(ori);
|
||||
if (aori % 2 == 0)
|
||||
{
|
||||
e1 = 0;
|
||||
@@ -1416,14 +1416,15 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
|
||||
{
|
||||
// Create a new auxiliary edge
|
||||
auxv2e[childPair] = auxEdges.size();
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
|
||||
parentEdge : -1 - parentEdge,
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ? parentEdge :
|
||||
FlipIndexSign(parentEdge),
|
||||
{childPair.first, childPair.second},
|
||||
{newParentEdge ? 0 : prevKI, ks}});
|
||||
}
|
||||
}
|
||||
|
||||
const int childEdge = childPairTopo ? v2e[childPair] : -1 - auxv2e[childPair];
|
||||
const int childEdge = childPairTopo ? v2e[childPair] :
|
||||
FlipIndexSign(auxv2e[childPair]);
|
||||
|
||||
// Check whether this is the final vertex in this parent edge. Note that
|
||||
// the logic for comparing (pv[0],pv[1]) to the next parents assumes the
|
||||
@@ -1460,14 +1461,15 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
|
||||
|
||||
// -1 denotes `ne` at endpoint
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
|
||||
-1 - parentEdge : parentEdge,
|
||||
FlipIndexSign(parentEdge) :
|
||||
parentEdge,
|
||||
{finalChildPair.first, finalChildPair.second},
|
||||
{ks, -1}});
|
||||
}
|
||||
}
|
||||
|
||||
const int finalChildEdge = finalChildPairTopo ? v2e[finalChildPair] :
|
||||
-1 - auxv2e[finalChildPair];
|
||||
FlipIndexSign(auxv2e[finalChildPair]);
|
||||
edgePairs.emplace_back(-1, -1, finalChildEdge, parentEdge);
|
||||
}
|
||||
|
||||
@@ -1805,7 +1807,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
auxFaces.push_back(auxFace);
|
||||
facePairs.emplace_back(
|
||||
FacePairInfo{cv[0], parentFace,
|
||||
SlaveFaceInfo{-1 - auxv2f[childPair],
|
||||
SlaveFaceInfo{FlipIndexSign(auxv2f[childPair]),
|
||||
0, {i0, j0}, {d0, d1}}});
|
||||
}
|
||||
}
|
||||
@@ -2111,7 +2113,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
auxv2e[childPair] = auxEdges.size();
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv0 < pv1 ?
|
||||
parentEdge :
|
||||
-1 - parentEdge,
|
||||
FlipIndexSign(parentEdge),
|
||||
{childPair.first, childPair.second},
|
||||
{knotIndex0, knotIndex1}});
|
||||
}
|
||||
@@ -2131,7 +2133,8 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
|
||||
const EdgePairInfo ep_e((e_idx == n_d - de) ? -1 : tv,
|
||||
(e_idx == n_d - de) ? -1 : tvki,
|
||||
-1 - auxv2e[childPair], parentEdge);
|
||||
FlipIndexSign(auxv2e[childPair]),
|
||||
parentEdge);
|
||||
|
||||
const bool unset = !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
|
||||
if (unset)
|
||||
@@ -2226,7 +2229,7 @@ void NCNURBSExtension::GetAuxFaceToPatchTable(Array2D<int> &auxface2patch)
|
||||
if (s < 0)
|
||||
{
|
||||
// Auxiliary face.
|
||||
const int aux = -1 - s;
|
||||
const int aux = FlipIndexSign(s);
|
||||
if (auxface2patch(aux, 0) >= 0)
|
||||
{
|
||||
if (auxface2patch(aux, 1) != -1) { consistent = false; }
|
||||
@@ -2316,7 +2319,7 @@ void NCNURBSExtension::UpdateAuxiliaryKnotSpans(const Array<int> &rf)
|
||||
for (auto auxEdge : auxEdges)
|
||||
{
|
||||
const int p = auxEdge.parent;
|
||||
const int parent = p < 0 ? -1 - p : p;
|
||||
const int parent = UnsignIndex(p);
|
||||
const int kv = KnotInd(parent);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
@@ -2382,14 +2385,8 @@ int NCNURBSExtension::AuxiliaryEdgeNE(int aux_edge)
|
||||
const int signedParentEdge = auxEdges[aux_edge].parent;
|
||||
const int ki0 = auxEdges[aux_edge].ksi[0];
|
||||
const int ki1raw = auxEdges[aux_edge].ksi[1];
|
||||
int ki1 = ki1raw;
|
||||
if (ki1raw == -1)
|
||||
{
|
||||
const bool rev = signedParentEdge < 0;
|
||||
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
|
||||
ki1 = KnotVec(parentEdge)->GetNE();
|
||||
}
|
||||
|
||||
const int ki1 = ki1raw == -1 ? KnotVec(UnsignIndex(signedParentEdge))->GetNE()
|
||||
: ki1raw;
|
||||
return ki1 - ki0;
|
||||
}
|
||||
|
||||
@@ -2403,7 +2400,7 @@ void NCNURBSExtension::SlaveEdgeToParent(int se, int parent,
|
||||
Array<int> sev(2);
|
||||
if (se < 0) // Auxiliary edge
|
||||
{
|
||||
for (int i=0; i<2; ++i) { sev[i] = auxEdges[-1 - se].v[i]; }
|
||||
for (int i=0; i<2; ++i) { sev[i] = auxEdges[FlipIndexSign(se)].v[i]; }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2459,7 +2456,7 @@ void NCNURBSExtension::GetMasterEdgePieceOffsets(int mid, Array<int> &os)
|
||||
}
|
||||
else
|
||||
{
|
||||
nes = AuxiliaryEdgeNE(-1 - s);
|
||||
nes = AuxiliaryEdgeNE(FlipIndexSign(s));
|
||||
}
|
||||
|
||||
os[i+1] = os[i] + nes;
|
||||
@@ -2565,7 +2562,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
|
||||
}
|
||||
else // Aux edge
|
||||
{
|
||||
const int aux_edge = -1 - s;
|
||||
const int aux_edge = FlipIndexSign(s);
|
||||
if (auxef[aux_edge].Size() == 0)
|
||||
{
|
||||
auxef[aux_edge].SetSize(AuxiliaryEdgeNE(aux_edge));
|
||||
@@ -2611,7 +2608,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
|
||||
}
|
||||
|
||||
MFEM_VERIFY(consistent, "");
|
||||
return partialChange ? -1 - dirSet : dirSet;
|
||||
return partialChange ? FlipIndexSign(dirSet) : dirSet;
|
||||
}
|
||||
|
||||
void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
@@ -2715,7 +2712,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
if (s < 0)
|
||||
{
|
||||
// Auxiliary face.
|
||||
const int aux = -1 - s;
|
||||
const int aux = FlipIndexSign(s);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
const int patch = auxface2patch(aux, i);
|
||||
@@ -2763,7 +2760,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
|
||||
const int dirSetSigned = SetPatchFactors(p);
|
||||
const bool partialChange = dirSetSigned < 0;
|
||||
const int dirSet = partialChange ? -1 - dirSetSigned : dirSetSigned;
|
||||
const int dirSet = UnsignIndex(dirSetSigned);
|
||||
const bool changed = (patchState[p] != dirSet) || partialChange;
|
||||
patchState[p] = dirSet;
|
||||
|
||||
@@ -2806,8 +2803,8 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
{
|
||||
const int dirSetSigned_i = SetPatchFactors(i);
|
||||
const bool partialChange_i = dirSetSigned_i < 0;
|
||||
const int dirSet_i = partialChange_i ? -1 - dirSetSigned_i :
|
||||
dirSetSigned_i;
|
||||
const int dirSet_i = partialChange_i ?
|
||||
FlipIndexSign(dirSetSigned_i) : dirSetSigned_i;
|
||||
const bool changed_i = (patchState[i] != dirSet_i) ||
|
||||
partialChange_i;
|
||||
patchState[p] = dirSet_i;
|
||||
@@ -3027,7 +3024,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
|
||||
|
||||
// Check whether ordering is reversed.
|
||||
const bool rev = verts[(s + 1) % 4] != fverts[1];
|
||||
if (rev) { s = -1 - s; } // Reversed order is encoded by the sign.
|
||||
if (rev) { s = FlipIndexSign(s); } // Reversed order is encoded by the sign.
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -3040,7 +3037,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
|
||||
// see GetFaceOrientation.
|
||||
bool Reorder2D(int ori, std::array<int, 2> &s0)
|
||||
{
|
||||
const int shift = ori < 0 ? -1 - ori : ori;
|
||||
const int shift = UnsignIndex(ori);
|
||||
|
||||
// Shift is an F1 index in the counter-clockwise ordering of 4 quad vertices.
|
||||
// Now find the (i,j) indices of this index, with i,j in {0,1}.
|
||||
@@ -3064,7 +3061,7 @@ void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
|
||||
int &n)
|
||||
{
|
||||
const bool rev = (signedShift < 0);
|
||||
const int shift = rev ? -1 - signedShift : signedShift;
|
||||
const int shift = UnsignIndex(signedShift);
|
||||
MFEM_ASSERT(0 <= shift && shift < 4, "");
|
||||
|
||||
// We consider 8 cases for the possible values of rev and shift.
|
||||
@@ -3136,7 +3133,7 @@ void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift,
|
||||
int& sm, int& sn, int& si, int& sj)
|
||||
{
|
||||
const bool rev = (signedShift < 0);
|
||||
const int shift = rev ? -1 - signedShift : signedShift;
|
||||
const int shift = UnsignIndex(signedShift);
|
||||
MFEM_ASSERT(0 <= shift && shift < 4, "");
|
||||
|
||||
// (0,0) <= (i,j) < (m,n) are old indices, and old vertex [shift] maps
|
||||
@@ -3798,8 +3795,7 @@ void NCNURBSExtension::GenerateOffsets()
|
||||
const int signedParentEdge = auxEdges[e].parent;
|
||||
const int ki0 = auxEdges[e].ksi[0];
|
||||
const int ki1raw = auxEdges[e].ksi[1];
|
||||
const bool rev = signedParentEdge < 0;
|
||||
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
|
||||
const int parentEdge = UnsignIndex(signedParentEdge);
|
||||
const int masterNE = KnotVec(parentEdge)->GetNE();
|
||||
const int ki1 = ki1raw == -1 ? masterNE : ki1raw;
|
||||
const int perEdgeCP = GetNCPperEdge(KnotVec(e));
|
||||
|
||||
+31
-18
@@ -43,13 +43,30 @@ KnotVector::KnotVector(istream &input)
|
||||
|
||||
KnotVector::KnotVector(int order, int NCP)
|
||||
{
|
||||
if (NCP == -1)
|
||||
{
|
||||
NumOfControlPoints = order + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
NumOfControlPoints = NCP;
|
||||
}
|
||||
Order = order;
|
||||
NumOfControlPoints = NCP;
|
||||
knot.SetSize(NumOfControlPoints + Order + 1);
|
||||
NumOfElements = 0;
|
||||
coarse = false;
|
||||
|
||||
knot = -1.;
|
||||
if (NCP == -1)
|
||||
{
|
||||
for (int i = 0 ; i < Order + 1; i++)
|
||||
{
|
||||
knot[i] = 0.0;
|
||||
knot[i + Order + 1] = 1.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
knot = -1.;
|
||||
}
|
||||
}
|
||||
|
||||
KnotVector::KnotVector(int order, const Vector &k)
|
||||
@@ -3706,10 +3723,7 @@ bool NURBSExtension::CheckPatches()
|
||||
for (int i = 0; i < edges.Size(); i++)
|
||||
{
|
||||
edges[i] = edge_to_ukv[edges[i]];
|
||||
if (oedge[i] < 0)
|
||||
{
|
||||
edges[i] = -1 - edges[i];
|
||||
}
|
||||
if (oedge[i] < 0) { edges[i] = FlipIndexSign(edges[i]); }
|
||||
}
|
||||
|
||||
// In 2d - opposite edges must be same knotvector with opposite sign.
|
||||
@@ -3723,7 +3737,7 @@ bool NURBSExtension::CheckPatches()
|
||||
// {7, 6}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7} for Geometry::CUBE in 3D
|
||||
// See fem/geom.cpp for these definitions.
|
||||
if ((dim == 2 &&
|
||||
(edges[0] != -1 - edges[2] || edges[1] != -1 - edges[3])) ||
|
||||
(edges[0] != FlipIndexSign(edges[2]) || edges[1] != FlipIndexSign(edges[3]))) ||
|
||||
|
||||
(dim == 3 &&
|
||||
(edges[0] != edges[2] || edges[0] != edges[4] ||
|
||||
@@ -3752,7 +3766,7 @@ void NURBSExtension::CheckBdrPatches()
|
||||
edges[i] = edge_to_ukv[edges[i]];
|
||||
if (oedge[i] < 0)
|
||||
{
|
||||
edges[i] = -1 - edges[i];
|
||||
edges[i] = FlipIndexSign(edges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4749,14 +4763,13 @@ void NURBSExtension::GenerateBdrElementDofTable()
|
||||
SetPatchToBdrElements();
|
||||
|
||||
int *dof = bel_dof->GetJ();
|
||||
int ndof = bel_dof->Size_of_connections();
|
||||
const int ndof = bel_dof->Size_of_connections();
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
int idx = dof[i];
|
||||
const int idx = dof[i];
|
||||
if (idx < 0)
|
||||
{
|
||||
dof[i] = -1 - (activeDof[-1-idx] - 1);
|
||||
dof[i] = -activeDof[-1-idx];
|
||||
dof[i] = -activeDof[FlipIndexSign(idx)];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4841,12 +4854,12 @@ void NURBSExtension::Generate2DBdrElementDofTable()
|
||||
for (int ii = 0; ii <= ord0; ii++)
|
||||
{
|
||||
conn.to = DofMap(p2g[(okv[0] >= 0) ? (i+ii) : (nx-i-ii)]);
|
||||
if (s == -1) { conn.to = -1 -conn.to; }
|
||||
if (s == -1) { conn.to = FlipIndexSign(conn.to); }
|
||||
bel_dof_list.Append(conn);
|
||||
}
|
||||
}
|
||||
bel_to_patch[lbe] = b;
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
|
||||
lbe++;
|
||||
}
|
||||
gbe++;
|
||||
@@ -4919,14 +4932,14 @@ void NURBSExtension::Generate3DBdrElementDofTable()
|
||||
{
|
||||
const int ii_ = (okv[0] >= 0) ? (i+ii) : (nx-i-ii);
|
||||
conn.to = DofMap(p2g(ii_, jj_));
|
||||
if (s == -1) { conn.to = -1 -conn.to; }
|
||||
if (s == -1) { conn.to = FlipIndexSign(conn.to); }
|
||||
bel_dof_list.Append(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
bel_to_patch[lbe] = b;
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
|
||||
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : (-1-j);
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
|
||||
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : FlipIndexSign(j);
|
||||
lbe++;
|
||||
}
|
||||
gbe++;
|
||||
|
||||
+11
-10
@@ -74,9 +74,13 @@ public:
|
||||
integers are read, for order and number of control points. */
|
||||
KnotVector(std::istream &input);
|
||||
|
||||
/** @brief Create a KnotVector with undefined knots (initialized to -1) of
|
||||
order @a order and number of control points @a NCP. */
|
||||
KnotVector(int order, int NCP);
|
||||
/** @brief Create a KnotVector with order @a order.
|
||||
When @a NCP is not provided the number of control points is set to
|
||||
@a order + 1, and the first @a order + 1 knots are set to 0 and last
|
||||
@a order + 1 knots are set to 1.
|
||||
When @a NCP is given number of control points is @a NCP and
|
||||
the knots are initialized to -1) */
|
||||
KnotVector(int order, int NCP = -1);
|
||||
|
||||
/** @brief Create a KnotVector with order @a order and knots @a knot.
|
||||
If @a k has the correct number of repeated knots at the begin and end,
|
||||
@@ -88,12 +92,10 @@ public:
|
||||
|
||||
/** @brief Create a KnotVector by passing in a degree, a Vector of interval
|
||||
lengths of length n, and a list of continuity of length n + 1.
|
||||
|
||||
The intervals refer to spans between unique knot values (not counting
|
||||
zero-size intervals at repeated knots), and the continuity values should
|
||||
be >= -1 (discontinuous) and <= order-1 (maximally-smooth for the given
|
||||
polynomial degree). Periodicity is not supported.
|
||||
*/
|
||||
polynomial degree). Periodicity is not supported.*/
|
||||
KnotVector(int order, const Vector& intervals,
|
||||
const Array<int>& continuity);
|
||||
|
||||
@@ -218,7 +220,7 @@ public:
|
||||
@a u.
|
||||
The main purpose of this function is its use in FindInterpolant.
|
||||
Use GetBotella instead for each shape function separately, perhaps in
|
||||
conjuction with GetSpan and GetRefPoint.*/
|
||||
conjunction with GetSpan and GetRefPoint.*/
|
||||
MFEM_DEPRECATED void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
|
||||
|
||||
/** @brief Global curve interpolation through the points @a x (overwritten).
|
||||
@@ -1396,8 +1398,7 @@ inline const real_t &NURBSPatch::operator()(int i, int j, int k, int l) const
|
||||
|
||||
inline int NURBSExtension::KnotInd(int edge) const
|
||||
{
|
||||
const int kv = edge_to_ukv[edge];
|
||||
return kv >= 0 ? kv : -1 - kv;
|
||||
return UnsignIndex(edge_to_ukv[edge]);
|
||||
}
|
||||
|
||||
inline int NURBSExtension::KnotSign(int edge) const
|
||||
@@ -1427,7 +1428,7 @@ const
|
||||
else
|
||||
{
|
||||
*okv = -oedge;
|
||||
return knotVectors[-1-kv];
|
||||
return knotVectors[FlipIndexSign(kv)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -400,7 +400,7 @@ void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
|
||||
}
|
||||
else // special case: prism edge-face constraint
|
||||
{
|
||||
if (entity_owner[1][-1-si] != MyRank)
|
||||
if (entity_owner[1][FlipIndexSign(si)] != MyRank)
|
||||
{
|
||||
master_flag |= 0x2;
|
||||
}
|
||||
@@ -571,9 +571,10 @@ void ParNCMesh::CalculatePMatrixGroups()
|
||||
ranks.SetSize(0);
|
||||
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
|
||||
{
|
||||
int si = face_list.slaves[j].index;
|
||||
int owner = (si >= 0) ? entity_owner[2][si] // standard face dependency
|
||||
/* */ : entity_owner[1][-1 - si]; // prism edge-face dep
|
||||
const int si = face_list.slaves[j].index;
|
||||
const int owner =
|
||||
(si >= 0) ? entity_owner[2][si] : // standard face dependency
|
||||
entity_owner[1][FlipIndexSign(si)]; // prism edge-face dep
|
||||
ranks.Append(groups[owner][0]);
|
||||
}
|
||||
ranks.Sort();
|
||||
@@ -1181,7 +1182,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
|
||||
|
||||
Mesh::FaceInfo &fi = pmesh.faces_info[cf.index];
|
||||
fi.Elem2No = -1 - fnbr_index[e[0]->index - NElements];
|
||||
fi.Elem2No = FlipIndexSign(fnbr_index[e[0]->index - NElements]);
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
@@ -1211,7 +1212,6 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
|
||||
|
||||
// enlarge Mesh::faces_info for ghost slaves
|
||||
MFEM_ASSERT(pmesh.faces_info.Size() == nfaces, "");
|
||||
MFEM_ASSERT(pmesh.GetNumFaces() == nfaces, "");
|
||||
pmesh.faces_info.SetSize(nfaces + nghosts);
|
||||
for (int i = nfaces; i < pmesh.faces_info.Size(); i++)
|
||||
@@ -1271,7 +1271,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
// In other words, side 1 IS the side that generated the face.
|
||||
}
|
||||
MFEM_ASSERT(fi.Elem2No >= NElements, "");
|
||||
fi.Elem2No = -1 - fnbr_index[fi.Elem2No - NElements];
|
||||
fi.Elem2No = FlipIndexSign(fnbr_index[fi.Elem2No - NElements]);
|
||||
|
||||
const DenseMatrix* pm = full_list.point_matrices[sf.geom][sf.matrix];
|
||||
if (!sloc && Dim == 3)
|
||||
@@ -1312,7 +1312,6 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
// Mesh::ApplyLocalSlaveTransformation.
|
||||
}
|
||||
|
||||
MFEM_ASSERT(fi.NCFace < 0, "fi.NCFace = " << fi.NCFace);
|
||||
fi.NCFace = pmesh.nc_faces_info.Size();
|
||||
pmesh.nc_faces_info.Append(Mesh::NCFaceInfo(true, sf.master, pm));
|
||||
}
|
||||
@@ -2288,7 +2287,7 @@ void ParNCMesh::Derefine(const Array<int> &derefs)
|
||||
if (element_type[index] == 0)
|
||||
{
|
||||
// this coarse element will get pruned, encode who owns it now
|
||||
index = -1 - elements[coarse[i]].rank;
|
||||
index = FlipIndexSign(elements[coarse[i]].rank);
|
||||
}
|
||||
transforms.embeddings[i].parent = index;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ add_subdirectory(hdiv-linear-solver)
|
||||
add_subdirectory(hooke)
|
||||
add_subdirectory(meshing)
|
||||
add_subdirectory(mtop)
|
||||
add_subdirectory(mtop/chpt)
|
||||
add_subdirectory(mtop/examples)
|
||||
add_subdirectory(multidomain)
|
||||
add_subdirectory(nurbs)
|
||||
add_subdirectory(parelag)
|
||||
|
||||
@@ -174,7 +174,6 @@ ParticleTrajectories::ParticleTrajectories(const ParticleSet &particles,
|
||||
|
||||
void ParticleTrajectories::AddSegmentStart()
|
||||
{
|
||||
if (!pset.GetNParticles()) { return; }
|
||||
// Create a new mesh for all particle segments for this timestep
|
||||
segment_meshes.emplace_front(1, pset.GetNParticles()*2,
|
||||
pset.GetNParticles(),
|
||||
@@ -200,11 +199,10 @@ void ParticleTrajectories::AddSegmentStart()
|
||||
|
||||
void ParticleTrajectories::SetSegmentEnd()
|
||||
{
|
||||
if (segment_meshes.empty()) { return; } // no segments to end
|
||||
|
||||
const Array<ParticleSet::IDType> &end_ids = pset.GetIDs();
|
||||
|
||||
// Add all endpoint vertices + segments for all particles
|
||||
// Add all endpoint vertices + segments for all particles that were in
|
||||
// SetSegmentStart
|
||||
int num_start = segment_ids.front().Size();
|
||||
for (int i = 0; i < num_start; i++)
|
||||
{
|
||||
@@ -230,11 +228,6 @@ void ParticleTrajectories::SetSegmentEnd()
|
||||
void ParticleTrajectories::Visualize()
|
||||
{
|
||||
SetSegmentEnd();
|
||||
if (segment_meshes.empty() && !mesh)
|
||||
{
|
||||
AddSegmentStart();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a mesh of all the trajectory segments
|
||||
std::vector<Mesh*> all_meshes;
|
||||
@@ -246,8 +239,23 @@ void ParticleTrajectories::Visualize()
|
||||
{
|
||||
all_meshes.push_back(mesh);
|
||||
}
|
||||
if (mesh_bb)
|
||||
{
|
||||
all_meshes.push_back(mesh_bb);
|
||||
}
|
||||
|
||||
Mesh trajectories(all_meshes.data(), all_meshes.size());
|
||||
bool vis = trajectories.GetNE() > 0;
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Allreduce(MPI_IN_PLACE, &vis, 1, MFEM_MPI_CXX_BOOL,
|
||||
MPI_LOR, pset.GetComm());
|
||||
#endif // MFEM_USE_MPI
|
||||
if (!vis) // if all rank have 0 elements, skip visualization
|
||||
{
|
||||
AddSegmentStart();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
VisualizeMesh(sock, vishost, visport, trajectories, comm,
|
||||
@@ -260,5 +268,97 @@ void ParticleTrajectories::Visualize()
|
||||
AddSegmentStart();
|
||||
}
|
||||
|
||||
void ParticleTrajectories::SetVisualizationBoundingBox(const Vector &xmin,
|
||||
const Vector &xmax)
|
||||
{
|
||||
MFEM_VERIFY(xmin.Size() == pset.GetDim() &&
|
||||
xmax.Size() == pset.GetDim(),
|
||||
"Bounding box dimension must match ParticleSet dimension.");
|
||||
|
||||
// Create a box mesh for visualization
|
||||
if (mesh_bb)
|
||||
{
|
||||
delete mesh_bb;
|
||||
mesh_bb = nullptr;
|
||||
}
|
||||
|
||||
if (pset.GetDim() == 2)
|
||||
{
|
||||
int dim = 2;
|
||||
int nvert = 4;
|
||||
int nelem = 4;
|
||||
mesh_bb = new Mesh(1, nvert, nelem, 0, dim);
|
||||
Vector v0(dim), v1(dim), v2(dim), v3(dim);
|
||||
v0 = xmin;
|
||||
v1 = xmax;
|
||||
v2[0] = xmax[0]; v2[1] = xmin[1];
|
||||
v3[0] = xmin[0]; v3[1] = xmax[1];
|
||||
|
||||
mesh_bb->AddVertex(v0);
|
||||
mesh_bb->AddVertex(v1);
|
||||
mesh_bb->AddVertex(v2);
|
||||
mesh_bb->AddVertex(v3);
|
||||
|
||||
int vi[2] = {0,1};
|
||||
mesh_bb->AddSegment(vi);
|
||||
vi[0] = 1; vi[1] = 2;
|
||||
mesh_bb->AddSegment(vi);
|
||||
vi[0] = 2; vi[1] = 3;
|
||||
mesh_bb->AddSegment(vi);
|
||||
vi[0] = 3; vi[1] = 0;
|
||||
mesh_bb->AddSegment(vi);
|
||||
mesh_bb->FinalizeMesh();
|
||||
}
|
||||
else // dim == 3
|
||||
{
|
||||
int dim = 3;
|
||||
int nvert = 8;
|
||||
int nelem = 12;
|
||||
mesh_bb = new Mesh(1, nvert, nelem, 0, dim);
|
||||
Vector v(dim);
|
||||
|
||||
// Vertices
|
||||
v[0] = xmin[0]; v[1] = xmin[1]; v[2] = xmin[2];
|
||||
mesh_bb->AddVertex(v); // 0: 000
|
||||
v[0] = xmax[0]; v[1] = xmin[1]; v[2] = xmin[2];
|
||||
mesh_bb->AddVertex(v); // 1: 100
|
||||
v[0] = xmax[0]; v[1] = xmax[1]; v[2] = xmin[2];
|
||||
mesh_bb->AddVertex(v); // 2: 110
|
||||
v[0] = xmin[0]; v[1] = xmax[1]; v[2] = xmin[2];
|
||||
mesh_bb->AddVertex(v); // 3: 010
|
||||
|
||||
v[0] = xmin[0]; v[1] = xmin[1]; v[2] = xmax[2];
|
||||
mesh_bb->AddVertex(v); // 4: 001
|
||||
v[0] = xmax[0]; v[1] = xmin[1]; v[2] = xmax[2];
|
||||
mesh_bb->AddVertex(v); // 5: 101
|
||||
v[0] = xmax[0]; v[1] = xmax[1]; v[2] = xmax[2];
|
||||
mesh_bb->AddVertex(v); // 6: 111
|
||||
v[0] = xmin[0]; v[1] = xmax[1]; v[2] = xmax[2];
|
||||
mesh_bb->AddVertex(v); // 7: 011
|
||||
|
||||
// Segments
|
||||
int vi[2];
|
||||
// Bottom face
|
||||
vi[0] = 0; vi[1] = 1; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 1; vi[1] = 2; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 2; vi[1] = 3; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 3; vi[1] = 0; mesh_bb->AddSegment(vi);
|
||||
|
||||
// Top face
|
||||
vi[0] = 4; vi[1] = 5; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 5; vi[1] = 6; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 6; vi[1] = 7; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 7; vi[1] = 4; mesh_bb->AddSegment(vi);
|
||||
|
||||
// Vertical edges
|
||||
vi[0] = 0; vi[1] = 4; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 1; vi[1] = 5; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 2; vi[1] = 6; mesh_bb->AddSegment(vi);
|
||||
vi[0] = 3; vi[1] = 7; mesh_bb->AddSegment(vi);
|
||||
|
||||
mesh_bb->FinalizeMesh();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace common
|
||||
} // namespace mfem
|
||||
|
||||
@@ -46,7 +46,8 @@ class ParticleTrajectories
|
||||
{
|
||||
protected:
|
||||
const ParticleSet &pset;
|
||||
Mesh *mesh = nullptr;
|
||||
Mesh *mesh = nullptr; // optional edge mesh to visualize along with particles
|
||||
Mesh *mesh_bb = nullptr; // optional bounding box mesh for visualization
|
||||
|
||||
socketstream sock;
|
||||
/// Track particle IDs that exist at the segment start.
|
||||
@@ -90,10 +91,24 @@ public:
|
||||
const char *keys_=nullptr);
|
||||
|
||||
/// Add a mesh to be visualized along with the particle trajectories.
|
||||
void AddMeshForVisualization(Mesh *mesh_) { mesh = mesh_; }
|
||||
void AddMeshForVisualization(Mesh *mesh_)
|
||||
{
|
||||
MFEM_VERIFY(mesh_->Dimension() == 1,
|
||||
"Mesh dimension must be 1 to match the particle trajectory.");
|
||||
mesh = mesh_;
|
||||
}
|
||||
|
||||
/// Visualize the particle trajectories (and mesh if provided).
|
||||
void Visualize();
|
||||
|
||||
/// Set the bounding box for visualization.
|
||||
void SetVisualizationBoundingBox(const Vector &xmin, const Vector &xmax);
|
||||
|
||||
/// Destructor
|
||||
~ParticleTrajectories()
|
||||
{
|
||||
delete mesh_bb;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -34,11 +34,13 @@ if (MFEM_USE_MPI)
|
||||
EXTRA_HEADERS maxwell_solver.hpp ${MFEM_MINIAPPS_COMMON_HEADERS}
|
||||
LIBRARIES mfem-common)
|
||||
|
||||
add_mfem_miniapp(lorentz
|
||||
MAIN lorentz.cpp
|
||||
EXTRA_HEADERS ${MFEM_MINIAPPS_COMMON_HEADERS}
|
||||
LIBRARIES mfem-common)
|
||||
|
||||
if (MFEM_USE_GSLIB)
|
||||
add_mfem_miniapp(lorentz
|
||||
MAIN lorentz.cpp
|
||||
EXTRA_HEADERS ${MFEM_MINIAPPS_COMMON_HEADERS}
|
||||
LIBRARIES mfem-common)
|
||||
endif()
|
||||
|
||||
# Add the corresponding tests to the "test" target
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME tesla_np=4
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,10 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_MINIAPPS =
|
||||
PAR_MINIAPPS = volta tesla maxwell joule lorentz
|
||||
PAR_MINIAPPS = volta tesla maxwell joule
|
||||
ifeq ($(MFEM_USE_GSLIB), YES)
|
||||
PAR_MINIAPPS += lorentz
|
||||
endif
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
@@ -51,9 +54,11 @@ all: $(MINIAPPS)
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $@.o $@_solver.o $(COMMON_LIB) \
|
||||
$(MFEM_LIBS)
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),YES)
|
||||
lorentz: %: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<)
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $@.o $(COMMON_LIB) $(MFEM_LIBS)
|
||||
endif
|
||||
|
||||
# Rules for compiling miniapp dependencies
|
||||
$(addsuffix _solver.o,$(MINIAPPS)): \
|
||||
@@ -112,10 +117,10 @@ joule-test-par: joule
|
||||
lorentz-test-par: lorentz-test-1 lorentz-test-2
|
||||
lorentz-test-1: lorentz volta-test-3
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Electromagnetic miniapp,\
|
||||
-er Volta-AMR-Parallel -ec 2 -x0 '0.5 0.5 0.9' -p0 '1 0 0')
|
||||
-er Volta-AMR-Parallel -ec 2 -npt 100 -xmin '0.0 0.0 0.0' -xmax '1.0 1.0 1.0' -pmin '1 0 0' -pmax '1 0 0' -rdf 0 -vt 0 -nt 100')
|
||||
lorentz-test-2: lorentz tesla-test-2
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Electromagnetic miniapp,\
|
||||
-br Tesla-AMR-Parallel -bc 2 -x0 '0.1 0.5 0.1' -p0 '0 0.4 0.1' -tf 9)
|
||||
-br Tesla-AMR-Parallel -bc 2 -br Tesla-AMR-Parallel -npt 10 -xmin '0.0 0.0 0.0' -xmax '1.0 1.0 1.0' -pmin '0 0.1 0.05' -pmax '0 0.4 0.1' -nt 1000 -rdf 0 -vt 0)
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
|
||||
@@ -421,22 +421,18 @@ void NavierParticles::Step(const real_t dt, const ParGridFunction &u_gf,
|
||||
void NavierParticles::InterpolateUW(const ParGridFunction &u_gf,
|
||||
const ParGridFunction &w_gf)
|
||||
{
|
||||
finder.FindPoints(X(), X().GetOrdering());
|
||||
finder.FindPoints(X());
|
||||
|
||||
finder.Interpolate(u_gf, U());
|
||||
Ordering::Reorder(U(), U().GetVDim(), u_gf.ParFESpace()->GetOrdering(),
|
||||
U().GetOrdering());
|
||||
finder.Interpolate(u_gf, U(), U().GetOrdering());
|
||||
|
||||
finder.Interpolate(w_gf, W());
|
||||
Ordering::Reorder(W(), W().GetVDim(), w_gf.ParFESpace()->GetOrdering(),
|
||||
W().GetOrdering());
|
||||
finder.Interpolate(w_gf, W(), W().GetOrdering());
|
||||
}
|
||||
|
||||
void NavierParticles::DeactivateLostParticles(bool findpts)
|
||||
{
|
||||
if (findpts)
|
||||
{
|
||||
finder.FindPoints(X(), X().GetOrdering());
|
||||
finder.FindPoints(X());
|
||||
}
|
||||
|
||||
const Array<unsigned int> lost_idxs = finder.GetPointsNotFoundIndices();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
if(MFEM_USE_MPI)
|
||||
|
||||
list(APPEND AN_SOURCES linear_anisotropic_elasticity.hpp)
|
||||
list(APPEND AN_HEADERS linear_anisotropic_elasticity.cpp)
|
||||
|
||||
convert_filenames_to_full_paths(AN_SOURCES)
|
||||
convert_filenames_to_full_paths(AN_HEADERS)
|
||||
|
||||
set(EX_COMMON_FILES
|
||||
EXTRA_SOURCES ${AN_SOURCES}
|
||||
EXTRA_HEADERS ${AN_HEADERS})
|
||||
|
||||
add_mfem_miniapp(mtop_test_anisotropic_le
|
||||
MAIN test_anisotropic_le.cpp
|
||||
${EX_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
|
||||
endif (MFEM_USE_MPI)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user