Compare commits

..
139 changed files with 1578 additions and 15976 deletions
-1
View File
@@ -369,7 +369,6 @@ 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
-20
View File
@@ -17,9 +17,6 @@ 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
@@ -114,23 +111,6 @@ 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
+1 -5
View File
@@ -9,7 +9,6 @@
// 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
@@ -19,7 +18,6 @@
// 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
@@ -27,8 +25,6 @@
//
// 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
@@ -197,7 +193,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 && (!ea || hybridization))
if (!pa)
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
+1 -6
View File
@@ -9,7 +9,6 @@
// 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
@@ -18,18 +17,14 @@
// 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
@@ -235,7 +230,7 @@ int main(int argc, char *argv[])
pcg->SetMaxIter(2000);
pcg->SetPrintLevel(1);
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
else if (pa || ea) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
else
{
ParFiniteElementSpace *prec_fespace =
+6 -35
View File
@@ -825,46 +825,14 @@ 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);
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);
ConstrainedOperator A_constrained(this, ess_tdof_list);
A_constrained.EliminateRHS(x, b);
hybridization->ReduceRHS(b, B);
X.SetSize(B.Size());
X = 0.0;
}
@@ -874,6 +842,7 @@ 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
@@ -909,6 +878,7 @@ 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);
@@ -921,6 +891,7 @@ 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());
+28 -67
View File
@@ -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(ncp, nb);
ubound.SetSize(ncp, nb);
lbound.SetSize(nb, ncp);
ubound.SetSize(nb, ncp);
nodes.SetSize(nb);
weights.SetSize(nb);
control_points.SetSize(ncp);
@@ -125,25 +125,21 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
{
if (j == 0)
{
lbound(j,i) = bv(i);
ubound(j,i) = bv(i);
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
}
else if (j == ncp-1)
{
lbound(j,i) = bv(i);
ubound(j,i) = bv(i);
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
}
else
{
vals(0) = bv(i);
vals(1) = bmv(i) + dm*bdmv(i);
vals(2) = bpv(i) + dp*bdpv(i);
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);
}
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
}
}
}
@@ -277,7 +273,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
intmax.SetSize(ncp);
intmin = 0.0;
intmax = 0.0;
Vector coeffm;
Vector coeffm(nb);
coeffm = 0.0;
real_t a0 = 0.0;
real_t a1 = 0.0;
@@ -305,8 +302,6 @@ 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;
@@ -347,8 +342,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(j,i)*c, ubound(j,i)*c);
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
}
}
}
@@ -479,10 +474,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(k,j);
vals(1) = w0*ubound(k,j);
vals(2) = w1*lbound(k,j);
vals(3) = w1*ubound(k,j);
vals(0) = w0*lbound(j,k);
vals(1) = w0*ubound(j,k);
vals(2) = w1*lbound(j,k);
vals(3) = w1*ubound(j,k);
intmin(k*ncp+i) += vals.Min();
intmax(k*ncp+i) += vals.Max();
}
@@ -558,17 +553,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes(i)-1; // x-coordinate
minNodalVals(i) -= a0V(j) + a1V(j)*x;
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
minBounds(i) -= a0V(j) + a1V(j)*x;
maxBounds(i) -= a0V(j) + a1V(j)*x;
}
// Compute Bernstein coefficients
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
lu.Solve(nb, 1, minNodalVals.GetData());
lu.Solve(nb, 1, maxNodalVals.GetData());
lu.Solve(nb, 1, minBounds.GetData());
lu.Solve(nb, 1, maxBounds.GetData());
for (int i = 0; i < nb; i++)
{
intminT(i*ncp2+j) = minNodalVals(i);
intmaxT(i*ncp2+j) = maxNodalVals(i);
intminT(i*ncp2+j) = minBounds(i);
intmaxT(i*ncp2+j) = maxBounds(i);
}
}
}
@@ -622,10 +617,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(k,j);
vals(1) = w0*ubound(k,j);
vals(2) = w1*lbound(k,j);
vals(3) = w1*ubound(k,j);
vals(0) = w0*lbound(j,k);
vals(1) = w0*ubound(j,k);
vals(2) = w1*lbound(j,k);
vals(3) = w1*ubound(j,k);
intmin(k*ncp2+i) += vals.Min();
intmax(k*ncp2+i) += vals.Max();
}
@@ -658,8 +653,7 @@ 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);
@@ -672,39 +666,6 @@ 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];
@@ -755,4 +716,4 @@ void PLBound::Print(std::ostream &outp) const
ubound.Print(outp);
}
}
}
+20 -71
View File
@@ -19,18 +19,14 @@ 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.
@@ -39,9 +35,7 @@ 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.
@@ -60,7 +54,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; // ncp x nb matrices with bounds of all bases
DenseMatrix lbound, ubound; // nb x ncp 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
@@ -86,9 +80,6 @@ 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,
@@ -101,82 +92,40 @@ 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;
/** @brief Enable (default) or disable linear projection before bounding.
*
* @details This projection increases the computational cost but results in
* tighter bounds.
*/
// Enable (default) or disable linear projection before bounding.
// This projection increases the computational cost but results in tighter
// bounds.
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
/** @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.
*/
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D/2D/3D.
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:
/** @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.
*/
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D.
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/** @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.
*/
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 2D.
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/** @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.
*/
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 3D.
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
* for Bernstein bases.
*/
/// 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,
+41 -215
View File
@@ -84,33 +84,29 @@ public:
const std::vector<derivative_action_t> &derivative_actions,
const FieldDescriptor &direction,
const int &daction_l_size,
const int &derivative_action_tr_l_size,
const std::vector<derivative_action_t> &derivative_tr_actions,
const std::vector<derivative_action_t> &derivative_actions_transpose,
const FieldDescriptor &transpose_direction,
const int &daction_transpose_l_size,
const std::vector<Vector *> &solutions_l,
const std::vector<Vector *> &parameters_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 assemble_derivative_hypreparmatrix_callback_t
&assemble_derivative_hypreparmatrix_callback) :
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
&assemble_derivative_hypreparmatrix_callbacks) :
Operator(height, width),
derivative_actions(derivative_actions),
direction(direction),
daction_l(daction_l_size),
daction_l_size(daction_l_size),
derivative_action_tr_l_size(derivative_action_tr_l_size),
derivative_tr_actions(derivative_tr_actions),
derivative_actions_transpose(derivative_actions_transpose),
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_callback(
assemble_derivative_hypreparmatrix_callback)
assemble_derivative_hypreparmatrix_callbacks(
assemble_derivative_hypreparmatrix_callbacks)
{
std::vector<Vector> s_l(solutions_l.size());
for (size_t i = 0; i < s_l.size(); i++)
@@ -160,18 +156,18 @@ public:
/// direction_t on T-dofs.
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
{
MFEM_ASSERT(!derivative_tr_actions.empty(),
MFEM_ASSERT(!derivative_actions_transpose.empty(),
"derivative can't be used to be multiplied in transpose mode");
daction_l.SetSize(derivative_action_tr_l_size);
daction_l.SetSize(width);
daction_l = 0.0;
prolongation(transpose_direction, direction_t, direction_l);
for (const auto &f : derivative_tr_actions)
for (const auto &f : derivative_actions_transpose)
{
f(fields_e, direction_l, daction_l);
}
tr_prolongation_transpose(daction_l, result_t);
prolongation_transpose(daction_l, result_t);
};
/// @brief Assemble the derivative operator into a SparseMatrix.
@@ -187,10 +183,6 @@ 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.
@@ -199,7 +191,13 @@ public:
/// be an uninitialized object.
void Assemble(HypreParMatrix *&A)
{
assemble_derivative_hypreparmatrix_callback(fields_e, 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);
}
}
private:
@@ -214,12 +212,10 @@ 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_tr_actions;
std::vector<derivative_action_t> derivative_actions_transpose;
FieldDescriptor transpose_direction;
@@ -229,15 +225,13 @@ 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.
assemble_derivative_hypreparmatrix_callback_t
assemble_derivative_hypreparmatrix_callback;
std::vector<assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callbacks;
};
/// Class representing a differentiable operator which acts on solution and
@@ -463,10 +457,7 @@ public:
dir_l = s_l[derivative_idx];
}
for (size_t i = 0; i < derivative_setup_callbacks[derivative_id].size(); i++)
{
derivative_setup_callbacks[derivative_id][i](fields_e, dir_l);
}
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
return std::make_shared<DerivativeOperator>(
height,
@@ -474,17 +465,15 @@ public:
derivative_action_callbacks[derivative_id],
fields[derivative_idx],
residual_l.Size(),
derivative_action_tr_l_size[derivative_id],
derivative_action_tr_callbacks[derivative_id],
daction_transpose_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_callback[derivative_id]);
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
}
private:
@@ -497,15 +486,13 @@ private:
std::map<size_t,
std::vector<derivative_action_t>> derivative_action_callbacks;
std::map<size_t,
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::vector<derivative_action_t>> daction_transpose_callbacks;
std::map<size_t,
std::vector<assemble_derivative_sparsematrix_callback_t>>
assemble_derivative_sparsematrix_callbacks;
std::map<size_t, assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callback;
std::map<size_t,
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
assemble_derivative_hypreparmatrix_callbacks;
std::vector<FieldDescriptor> solutions;
std::vector<FieldDescriptor> parameters;
@@ -523,7 +510,7 @@ private:
std::function<void(Vector &, Vector &)> output_restriction_transpose;
restriction_callback_t restriction_callback;
std::map<size_t, std::vector<Vector>> derivative_qp_caches;
std::map<size_t, Vector> derivative_qp_caches;
std::map<size_t, size_t> assembled_vector_sizes;
@@ -782,10 +769,9 @@ 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, num_qp,
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
Vector shmem_cache(action_shmem_info.total_size);
@@ -908,10 +894,9 @@ 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,
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
num_qp, input_size_on_qp, residual_size_on_qp,
element_dof_ordering, d_field_idx);
@@ -983,12 +968,9 @@ 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].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;
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
trial_vdim *
total_trial_op_dim * num_qp * num_entities);
// Create local references for MSVC lambda capture compatibility
auto& fields_ref = this->fields;
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
@@ -1030,8 +1012,6 @@ 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
@@ -1044,7 +1024,7 @@ void DifferentiableOperator::AddIntegrator(
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].ReadWrite(), test_vdim, test_op_dim,
auto qpdc = Reshape(qpdc_mem.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);
@@ -1115,7 +1095,6 @@ 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
@@ -1133,7 +1112,7 @@ void DifferentiableOperator::AddIntegrator(
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
auto qpdc = Reshape(qpdc_mem.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);
@@ -1178,154 +1157,6 @@ 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:
@@ -1359,7 +1190,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
@@ -1371,7 +1202,7 @@ void DifferentiableOperator::AddIntegrator(
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
auto qpdc = Reshape(qpdc_mem.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);
@@ -1419,10 +1250,7 @@ void DifferentiableOperator::AddIntegrator(
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
if (A == nullptr)
{
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
}
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);
@@ -1493,15 +1321,14 @@ void DifferentiableOperator::AddIntegrator(
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
}
}
// Don't finalize here since multiple callbacks might contribute to the same matrix
// A->Finalize() will be called after all callbacks have contributed
A->Finalize();
});
// Create local references for MSVC lambda capture compatibility
auto& assemble_derivative_sparsematrix_callbacks_ref =
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
assemble_derivative_hypreparmatrix_callback[derivative_id] =
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
[
input_is_dependent,
input_to_field,
@@ -1515,7 +1342,6 @@ void DifferentiableOperator::AddIntegrator(
{
f(f_e, spmat);
}
spmat->Finalize();
if (spmat == nullptr)
{
@@ -1569,7 +1395,7 @@ void DifferentiableOperator::AddIntegrator(
trial_fes->Dof_TrueDof_Matrix());
}
delete spmat;
};
});
}, derivative_ids);
}
}
-61
View File
@@ -530,65 +530,4 @@ 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
+17 -12
View File
@@ -505,13 +505,13 @@ void map_field_to_quadrature_data(
}
}
template <typename field_operator_ts, size_t N, size_t M>
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
MFEM_HOST_DEVICE inline
void map_fields_to_quadrature_data(
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,
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,
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<N>([&](auto i)
for_constexpr<num_inputs>([&](auto i)
{
const DeviceTensor<1> &field_e =
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
@@ -549,7 +549,12 @@ void map_fields_to_quadrature_data(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
integration_weights, scratch_mem);
}
else { MFEM_ABORT_KERNEL("unsupported dimension"); }
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("unsupported dimension");
#endif
}
}
else
{
@@ -622,20 +627,20 @@ void map_fields_to_quadrature_data_conditional(
});
}
template <size_t N, typename field_operator_ts>
template <size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_direction_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, N> &directions_qp,
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
const DeviceTensor<1> &direction_e,
const std::array<DofToQuadMap, N> &dtqmaps,
const std::array<DofToQuadMap, num_inputs> &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, N> &conditions,
const std::array<bool, num_inputs> &conditions,
const int &dimension,
const bool &use_sum_factorization)
{
for_constexpr<N>([&](auto i)
for_constexpr<num_inputs>([&](auto i)
{
if (conditions[i])
{
+1 -1
View File
@@ -82,7 +82,7 @@ protected:
};
/// @brief Uniform parameter space
class UniformParameterSpace final : public ParameterSpace
class UniformParameterSpace : public ParameterSpace
{
public:
/// @brief Constructor for a uniform parameter space
+31 -81
View File
@@ -379,102 +379,54 @@ 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 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 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 q the current quadrature point index.
/// @param transpose switch to use transpose action.
template <size_t N>
template <size_t num_fields>
MFEM_HOST_DEVICE inline
void apply_qpdc(
DeviceTensor<3> &fhat,
const std::array<DeviceTensor<2>, N> &shadow_shmem,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
const DeviceTensor<5, const real_t> &qpdc,
const DeviceTensor<1, const real_t> &op_dims,
const int &q,
bool transpose)
const DeviceTensor<1, const real_t> &itod,
const int &q)
{
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 total_trial_op_dim = qpdc.GetShape()[3];
const int num_qp = qpdc.GetShape()[4];
const size_t num_inputs = itod.GetShape()[0];
if (transpose)
for (int i = 0; i < test_vdim; i++)
{
for (int j = 0; j < trial_vdim; j++)
for (int k = 0; k < test_op_dim; k++)
{
for (int m = 0; m < total_trial_op_dim; m++)
real_t sum = 0.0;
int m_offset = 0;
for (size_t s = 0; s < num_inputs; s++)
{
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++)
const int trial_op_dim = static_cast<int>(itod(s));
if (trial_op_dim == 0)
{
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;
continue;
}
}
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 auto d_qp =
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
for (int j = 0; j < trial_vdim; j++)
{
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++)
{
for (int m = 0; m < trial_op_dim; m++)
{
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
}
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
}
m_offset += trial_op_dim;
}
fhat(i, k, q) = sum;
m_offset += trial_op_dim;
}
fhat(i, k, q) = sum;
}
}
}
} // namespace detail
}
/// @brief Apply the quadrature point data cache (qpdc) to a vector
/// (usually a direction).
@@ -493,18 +445,16 @@ void apply_qpdc(
/// @param q1d number of quadrature points in 1D.
/// @param dimension spatial dimension.
/// @param use_sum_factorization whether to use sum factorization.
/// @param T switch to use transpose application.
template <size_t N>
template <size_t num_fields>
MFEM_HOST_DEVICE inline
void apply_qpdc(
DeviceTensor<3> &fhat,
const std::array<DeviceTensor<2>, N> &shadow_shmem,
const std::array<DeviceTensor<2>, num_fields> &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 T = false)
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
@@ -512,7 +462,7 @@ void apply_qpdc(
{
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
{
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
else if (dimension == 2)
@@ -522,7 +472,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, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
@@ -535,7 +485,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, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
@@ -550,7 +500,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, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
+16 -29
View File
@@ -243,35 +243,6 @@ 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)
@@ -356,4 +327,20 @@ 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
+3 -9
View File
@@ -1202,14 +1202,7 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
const Operator *P = get_prolongation(f);
auto PT = [=](const Vector &r_local, Vector &y)
{
if (P)
{
P->MultTranspose(r_local, y);
}
else
{
y = r_local;
}
P->MultTranspose(r_local, y);
};
return PT;
}
@@ -1587,13 +1580,14 @@ 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>
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs, typename input_t>
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,
+1 -1
View File
@@ -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<NB*NB>(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
{
// Perform change of basis if needed
if (CHANGE_BASIS)
+3 -3
View File
@@ -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 the element (where the local face is face_id), return the
/// corresponding face DOF index ordered lexicographically relative to the face
/// itself.
/// 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.
MFEM_HOST_DEVICE
inline int PermuteFace2D(const int face_id, const int orientation,
const int size1d, const int index)
+71 -60
View File
@@ -231,7 +231,7 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
{
for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0) + hess(nd,3) + hess(nd,5);
Laplacian[nd] = hess(nd,0) + hess(nd,4) + hess(nd,5);
}
}
else if (dim == 2)
@@ -268,9 +268,11 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
scale[0] = Gij(0,0);
scale[1] = 2*Gij(0,1);
scale[2] = 2*Gij(0,2);
scale[3] = Gij(1,1);
scale[4] = 2*Gij(1,2);
scale[5] = Gij(2,2);
scale[3] = 2*Gij(1,2);
scale[4] = Gij(2,2);
scale[5] = Gij(1,1);
}
else if (dim == 2)
{
@@ -307,12 +309,12 @@ void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
map[2] = 2;
map[3] = 1;
map[4] = 3;
map[5] = 4;
map[4] = 5;
map[5] = 3;
map[6] = 2;
map[7] = 4;
map[8] = 5;
map[7] = 3;
map[8] = 4;
}
else if (dim == 2)
{
@@ -380,7 +382,11 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
}
if (!d2q)
{
#ifdef MFEM_THREAD_SAFE
@@ -655,67 +661,58 @@ 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
{
// 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)
// 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++)
{
// 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 d = 0; d < b_dim; d++)
{
for (int d = 0; d < b_dim; d++)
for (int j = 0; j < dof; j++)
{
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 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)
{
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);
}
const int g_dim = [this]()
{
switch (deriv_type)
{
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);
}
}
@@ -727,7 +724,13 @@ const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
//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;
}
}
if (d2q) { return *d2q; }
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
@@ -2628,7 +2631,15 @@ const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
for (int i = 0; i < dof2quad_array.Size(); i++)
{
auto* d2q_ = dof2quad_array[i];
if (d2q_->IntRule == &ir && d2q_->mode == mode)
{
d2q = d2q_;
break;
}
}
if (!d2q)
{
d2q = new DofToQuad;
-22
View File
@@ -222,12 +222,6 @@ 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
@@ -413,7 +407,6 @@ 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;
@@ -1383,21 +1376,6 @@ 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
-48
View File
@@ -60,12 +60,6 @@ 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)
{
@@ -93,11 +87,6 @@ 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)
@@ -1267,12 +1256,6 @@ 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
{
@@ -1649,37 +1632,6 @@ 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
+1 -9
View File
@@ -50,8 +50,6 @@ 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
@@ -72,8 +70,6 @@ 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; }
};
@@ -408,9 +404,6 @@ 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; }
@@ -452,8 +445,7 @@ 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
View File
@@ -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*(dim+1))/2);
DenseMatrix ddu(dof, dim);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x, ddshape_x);
+4 -3
View File
@@ -445,10 +445,11 @@ 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) );
}
}
}
+32 -63
View File
@@ -282,7 +282,14 @@ 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++) { vdof[i] = UnsignIndex(vdof[i]); }
for (int i = 0; i < n; i++)
{
int j;
if ((j = vdof[i]) < 0)
{
vdof[i] = -1-j;
}
}
}
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
@@ -476,14 +483,13 @@ void FiniteElementSpace::ReorderElementToDofTable()
for (int k = 0, dof_counter = 0; k < nnz; k++)
{
const int sdof = J[k]; // signed dof
const int dof = UnsignIndex(sdof);
const int dof = (sdof < 0) ? -1-sdof : sdof;
int new_dof = dof_marker[dof];
if (new_dof < 0)
{
dof_marker[dof] = new_dof = dof_counter++;
}
// Preserve the sign of sdof
J[k] = (sdof < 0) ? FlipIndexSign(new_dof) : new_dof;
J[k] = (sdof < 0) ? -1-new_dof : new_dof; // preserve the sign of sdof
}
}
@@ -541,7 +547,7 @@ void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
{
for (auto d : dofs)
{
mark_array[UnsignIndex(d)] = -1;
mark_array[d >= 0 ? d : -1 - d] = -1;
}
}
@@ -925,7 +931,7 @@ void FiniteElementSpace::AddDependencies(
if (std::abs(coef) > 1e-12)
{
const int mdof = master_dofs[j];
if (mdof != sdof && mdof != FlipIndexSign(sdof))
if (mdof != sdof && mdof != (-1-sdof))
{
deps.Add(sdof, mdof, coef);
}
@@ -1018,7 +1024,7 @@ int FiniteElementSpace::GetDegenerateFaceDofs(int index, Array<int> &dofs,
// FiniteElementSpace::AddDependencies.
Array<int> edof;
int order = GetEdgeDofs(FlipIndexSign(index), edof, variant);
int order = GetEdgeDofs(-1 - index, edof, variant);
int nv = fec->DofForGeometry(Geometry::POINT);
int ne = fec->DofForGeometry(Geometry::SEGMENT);
@@ -1510,76 +1516,36 @@ const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
const bool is_dg_space = IsDGSpace();
const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
L2FaceValues::DoubleValued : L2FaceValues::SingleValued;
auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
key_face key = std::make_tuple(is_dg_space, f_ordering, type, m);
auto itr = L2F.find(key);
if (itr != L2F.end())
{
return itr->second.get();
return itr->second;
}
else
{
std::unique_ptr<FaceRestriction> res;
FaceRestriction *res;
if (is_dg_space)
{
if (Conforming())
{
res.reset(new L2FaceRestriction(*this, f_ordering, type, m));
res = new L2FaceRestriction(*this, f_ordering, type, m);
}
else
{
res.reset(new NCL2FaceRestriction(*this, f_ordering, type, m));
res = new NCL2FaceRestriction(*this, f_ordering, type, m);
}
}
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
{
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
}
else
{
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
res = new ConformingFaceRestriction(*this, f_ordering, type);
}
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;
L2F[key] = res;
return res;
}
}
@@ -1704,8 +1670,8 @@ SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
for (int i = 0; i < fine_ldof; i++)
{
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
if (!mark[m])
{
@@ -1766,7 +1732,7 @@ SparseMatrix *FiniteElementSpace::VariableOrderRefinementMatrix(
for (int i = 0; i < fine_ldof; i++)
{
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
int m = (r >= 0) ? r : (-1 - r);
if (!mark[m])
{
@@ -2476,8 +2442,8 @@ SparseMatrix* FiniteElementSpace::DerefinementMatrix(int old_ndofs,
{
if (!std::isfinite(lR(i, 0))) { continue; }
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
@@ -3195,7 +3161,7 @@ void FiniteElementSpace::CalcEdgeFaceVarOrders(
else
{
// degenerate face (i.e., edge-face constraint)
slave_orders |= edge_orders[FlipIndexSign(slave.index)];
slave_orders |= edge_orders[-1 - slave.index];
}
}
@@ -4003,8 +3969,11 @@ 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];
+13 -10
View File
@@ -13,7 +13,6 @@
#define MFEM_FESPACE
#include "../config/config.hpp"
#include "../general/hash_util.hpp"
#include "../linalg/ordering.hpp"
#include "../linalg/sparsemat.hpp"
#include "../mesh/mesh.hpp"
@@ -321,11 +320,18 @@ protected:
mutable OperatorHandle L2E_nat, L2E_lex;
/// The face restriction operators, see GetFaceRestriction().
using key_face = std::tuple<bool, ElementDofOrdering, FaceType, L2FaceValues>;
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;
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 Array<QuadratureInterpolator*> E2Q_array;
mutable Array<FaceQuadratureInterpolator*> E2IFQ_array;
@@ -745,9 +751,6 @@ 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
@@ -1150,7 +1153,7 @@ public:
/// Helper to return the DOF associated with a sign encoded DOF
static inline int DecodeDof(int dof)
{ return UnsignIndex(dof); }
{ return (dof >= 0) ? dof : (-1 - dof); }
/// Helper to determine the DOF and sign of a sign encoded DOF
static inline int DecodeDof(int dof, real_t& sign)
+60 -569
View File
@@ -30,7 +30,6 @@
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
namespace mfem
{
@@ -43,8 +42,9 @@ GridFunction::GridFunction(Mesh *m, std::istream &input)
// Grid functions are stored on the device
UseDevice(true);
fes = new FiniteElementSpace;
fec_owned = fes->Load(m, input);
owned_fes = std::make_shared<FiniteElementSpace>();
fes = owned_fes.get();
fec.reset(fes->Load(m, input));
skip_comment_lines(input, '#');
istream::int_type next_char = input.peek();
@@ -86,10 +86,11 @@ GridFunction::GridFunction(Mesh *m, GridFunction *gf_array[], int num_pieces)
int vdim, ordering;
fes = gf_array[0]->FESpace();
fec_owned = FiniteElementCollection::New(fes->FEColl()->Name());
fec.reset(FiniteElementCollection::New(fes->FEColl()->Name()));
vdim = fes->GetVDim();
ordering = fes->GetOrdering();
fes = new FiniteElementSpace(m, fec_owned, vdim, ordering);
owned_fes =
std::make_shared<FiniteElementSpace>(m, fec.get(), vdim, ordering);
SetSize(fes->GetVSize());
if (m->NURBSext)
@@ -156,13 +157,61 @@ GridFunction::GridFunction(Mesh *m, GridFunction *gf_array[], int num_pieces)
fes_sequence = fes->GetSequence();
}
GridFunction &GridFunction::operator=(const GridFunction &rhs)
{
if (&rhs != this)
{
Vector::operator=(rhs);
if (fes != rhs.fes)
{
fes = rhs.fes;
owned_fes = rhs.owned_fes;
fec = rhs.fec;
}
else
{
// ensure we don't accidentally delete if rhs doesn't have shared
// ownership
if (!owned_fes)
{
owned_fes = rhs.owned_fes;
}
if (!fec)
{
fec = rhs.fec;
}
}
fes_sequence = rhs.fes_sequence;
}
return *this;
}
void GridFunction::Destroy()
{
if (fec_owned)
owned_fes.reset();
fec.reset();
}
void GridFunction::MakeOwner()
{
if (fec.get() != fes->FEColl())
{
delete fes;
delete fec_owned;
fec_owned = NULL;
fec.reset(const_cast<FiniteElementCollection *>(fes->FEColl()));
}
if (owned_fes.get() != fes)
{
owned_fes.reset(fes);
}
}
void GridFunction::MakeOwner(FiniteElementCollection* fec_)
{
if (fec_)
{
MFEM_VERIFY(fec_ == fes->FEColl(),
"fec_ not associated with fes. If you intended to release "
"ownership, see GridFunction::ShareOwner");
MakeOwner();
}
}
@@ -4487,7 +4536,7 @@ std::unique_ptr<GridFunction> GridFunction::ProlongateToMaxOrder() const
PRefinementTransferOperator P(*fes, *fesMax);
P.Mult(*this, *xMax);
xMax->MakeOwner(fecMax);
xMax->MakeOwner();
return std::unique_ptr<GridFunction>(xMax);
}
@@ -5060,7 +5109,7 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
// assuming sol is scalar
solfes2d = new FiniteElementSpace(mesh2d, solfec2d);
sol2d = new GridFunction(solfes2d);
sol2d->MakeOwner(solfec2d);
sol2d->MakeOwner();
{
GridFunctionCoefficient csol(sol);
ExtrudeCoefficient c2d(mesh, csol, ny);
@@ -5118,103 +5167,6 @@ 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
@@ -5295,467 +5247,6 @@ 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);
}
}
+46 -146
View File
@@ -49,16 +49,15 @@ enum class ProjectType { DEFAULT, ELEMENT, GLOBAL_L2, ELEMENT_L2 };
class GridFunction : public Vector
{
protected:
/// FE space on which the grid function lives. Owned if #fec_owned is not NULL.
FiniteElementSpace *fes;
/// FE space on which the grid function lives.
FiniteElementSpace *fes = nullptr;
/** @brief Used when the grid function is read from a file. It can also be
set explicitly, see MakeOwner().
set explicitly, see MakeOwner(). */
std::shared_ptr<FiniteElementCollection> fec;
std::shared_ptr<FiniteElementSpace> owned_fes;
If not NULL, this pointer is owned by the GridFunction. */
FiniteElementCollection *fec_owned;
long fes_sequence; // see FiniteElementSpace::sequence, Mesh::sequence
long fes_sequence = 0; // see FiniteElementSpace::sequence, Mesh::sequence
/** Optional, internal true-dof vector: if the FiniteElementSpace #fes has a
non-trivial (i.e. not NULL) prolongation operator, this Vector may hold
@@ -95,20 +94,26 @@ protected:
void Destroy();
public:
GridFunction() { fes = NULL; fec_owned = NULL; fes_sequence = 0; UseDevice(true); }
GridFunction() { UseDevice(true); }
/// Copy constructor. The internal true-dof vector #t_vec is not copied.
GridFunction(const GridFunction &orig)
: Vector(orig), fes(orig.fes), fec_owned(NULL), fes_sequence(orig.fes_sequence)
: Vector(orig), fes(orig.fes), fec(orig.fec), owned_fes(orig.owned_fes),
fes_sequence(orig.fes_sequence)
{ UseDevice(true); }
GridFunction(GridFunction &&orig) = default;
/// Construct a GridFunction associated with the FiniteElementSpace @a *f.
GridFunction(FiniteElementSpace *f) : Vector(f->GetVSize())
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
GridFunction(FiniteElementSpace *f)
: Vector(f->GetVSize()), fes(f), fes_sequence(f->GetSequence())
{ UseDevice(true); }
/// Same as above but specify the memory type
GridFunction(FiniteElementSpace *f, MemoryType mt) : Vector(f->GetVSize(), mt)
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
GridFunction(FiniteElementSpace *f, MemoryType mt)
: Vector(f->GetVSize(), mt), fes(f), fes_sequence(f->GetSequence())
{ UseDevice(true); }
/// Construct a GridFunction using previously allocated array @a data.
/** The GridFunction does not assume ownership of @a data which is assumed to
@@ -117,14 +122,15 @@ public:
array can be replaced later using the method SetData().
*/
GridFunction(FiniteElementSpace *f, real_t *data)
: Vector(data, f->GetVSize())
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
: Vector(data, f->GetVSize()), fes(f), fes_sequence(f->GetSequence())
{ UseDevice(true); }
/** @brief Construct a GridFunction using previously allocated Vector @a base
starting at the given offset, @a base_offset. */
GridFunction(FiniteElementSpace *f, Vector &base, int base_offset = 0)
: Vector(base, base_offset, f->GetVSize())
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
: Vector(base, base_offset, f->GetVSize()), fes(f),
fes_sequence(f->GetSequence())
{ UseDevice(true); }
/// Construct a GridFunction on the given Mesh, using the data from @a input.
/** The content of @a input should be in the format created by the method
@@ -134,21 +140,25 @@ public:
GridFunction(Mesh *m, GridFunction *gf_array[], int num_pieces);
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
have the same size.
/// Copy assignment. Temporary data is not copied.
GridFunction &operator=(const GridFunction &rhs);
@note Defining this method overwrites the implicitly defined copy
assignment operator. */
GridFunction &operator=(const GridFunction &rhs)
{ return operator=((const Vector &)rhs); }
GridFunction &operator=(GridFunction &&gf) = default;
/// Make the GridFunction the owner of #fec_owned and #fes.
/** If the new FiniteElementCollection, @a fec_, is NULL, ownership of #fec_owned
and #fes is taken away. */
void MakeOwner(FiniteElementCollection *fec_) { fec_owned = fec_; }
/// Make the GridFunction a shared owner of #fec and #fes.
void MakeOwner();
[[deprecated("Use MakeOwner() instead")]]
void MakeOwner(FiniteElementCollection* fec_);
/// Gets a shared ownership of #owned_fes and #fec if this GridFunction has
/// shared ownership.
void ShareOwner(std::shared_ptr<FiniteElementSpace> &fes_,
std::shared_ptr<FiniteElementCollection> &fec_)
{
fes_ = owned_fes;
fec_ = fec;
}
FiniteElementCollection *OwnFEC() { return fec_owned; }
FiniteElementCollection* OwnFEC() { return fec.get(); }
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying #fes
int VectorDim() const;
@@ -564,70 +574,6 @@ 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(). */
@@ -1726,21 +1672,21 @@ public:
*/
///@{
/// Computes the \ref PLBound for the gridfunction with number of control
/// 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
/// 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
/// PLBound object used to compute the bounds.
/// We compute the bounds for each vdim if \p vdim < 1.
/// We compute the bounds for each vdim if @a 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 \p ref_factor, and returns the bounds for each element
/// ordered byNodes:
/// points based on @a ref_factor, and returns the bounds for each element
/// ordered byVDim:
/// 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 \p vdim < 1.
/// We compute the bounds for each vdim if @a vdim < 1.
PLBound GetElementBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const;
@@ -1751,18 +1697,6 @@ 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,
@@ -1770,45 +1704,11 @@ 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 byNodes:
/// are returned in @b lower and @b upper, ordered byVDim:
/// 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.
+7 -2
View File
@@ -273,12 +273,17 @@ 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 output ordering is determined from field_in.*/
the value is set to #default_interp_value. */
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);
/// Interpolation of field values, with output ordering from ParticleVector
virtual void Interpolate(const GridFunction &field_in,
ParticleVector &field_out)
{
Interpolate(field_in, field_out, field_out.GetOrdering());
}
/** 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. */
+1 -7
View File
@@ -789,6 +789,7 @@ 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, "");
@@ -822,13 +823,6 @@ 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
}
+273 -453
View File
File diff suppressed because it is too large Load Diff
+2 -20
View File
@@ -14,11 +14,8 @@
#include "../config/config.hpp"
#include "../general/array.hpp"
#include "../linalg/operator.hpp"
#include "../linalg/vector.hpp"
#include <memory>
namespace mfem
{
@@ -48,30 +45,15 @@ protected:
Array<int> hat_dof_gather_map;
Array<DofType> hat_dof_marker;
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.
Array<int> el_to_face;
Array<int> face_to_el;
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();
+5 -8
View File
@@ -1004,16 +1004,13 @@ 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, "");
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 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);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(NE,
Q1D, Q1D, Q1D,
[=] MFEM_HOST_DEVICE (int e)
mfem::forall_3D(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;
+6 -6
View File
@@ -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, "");
const auto b = b_.Read();
const auto d = d_.Read();
const auto x = x_.Read();
auto b = b_.Read();
auto d = d_.Read();
auto x = x_.Read();
auto y = y_.ReadWrite();
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
mfem::forall_2D(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, "");
const auto B = Reshape(basis.Read(), Q1D, D1D);
const auto D = Reshape(padata.Read(), Q1D, NE);
auto B = Reshape(basis.Read(), Q1D, D1D);
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)
{
+20 -98
View File
@@ -28,7 +28,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
const FaceType ftype = FaceType::Interior;
const int nf = mesh.GetNFbyType(ftype);
const Geometry::Type geom = mesh.GetTypicalFaceGeometry();
const Geometry::Type geom = mesh.GetFaceGeometry(0);
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.GetTypicalTraceElement();
const FiniteElement &trial_face_el = *trial_fes.GetFaceElement(0);
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.GetTypicalFE();
const FiniteElement &test_el = *test_fes.GetFE(0);
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.GetNumFacesWithGhost(); ++f)
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
if (!finfo.IsInterior() || finfo.IsNonconformingCoarse()) { continue; }
if (!finfo.IsInterior()) { 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();
emat = 0.0; // Will execute on device, since Write() sets the device flag
mfem::forall(emat.Size(), [=] MFEM_HOST_DEVICE (int i) { d_emat[i] = 0.0; });
}
const auto face_mats = Reshape(mass_emat.Read(), ndof_face, ndof_face, nf);
@@ -133,104 +133,26 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
}
};
auto permute_face_2 = [=] MFEM_HOST_DEVICE(int local_face_1, int local_face_2,
int orient, int size1d, int index)
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
{
if (dim == 2)
MFEM_FOREACH_THREAD(el_i, z, 2)
{
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)
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)
{
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)
// 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)
{
// 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);
}
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;
}
}
}
});
}
}
});
}
}
+5 -5
View File
@@ -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<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(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<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(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 constexpr (DIM == 2)
if (DIM == 2)
{
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
}
else if constexpr (DIM == 3)
else if (DIM == 3)
{
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
else { MFEM_ABORT("Unsupported kernel"); }
}
inline VectorDiffusionIntegrator::ApplyKernelType
+5 -5
View File
@@ -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<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(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<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(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 constexpr (DIM == 2)
if (DIM == 2)
{
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
}
else if constexpr (DIM == 3)
else if (DIM == 3)
{
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
else { MFEM_ABORT("Unsupported kernel"); }
}
inline VectorMassIntegrator::VectorMassAddMultPAType
+10 -6
View File
@@ -301,14 +301,18 @@ template <int DIM, int T_D1D, int T_Q1D>
DomainLFIntegrator::AssembleKernelType
DomainLFIntegrator::AssembleKernels::Kernel()
{
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>; }
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>;
}
MFEM_ABORT("");
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif // MFEM_LININTEG_DOMAIN_KERNELS_HPP
#endif
+31 -3
View File
@@ -14,7 +14,6 @@
#include "../config/config.hpp"
#include "kernel_reporter.hpp"
#include "../general/hash_util.hpp"
#include <unordered_map>
#include <tuple>
#include <type_traits>
@@ -87,6 +86,35 @@ 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 { };
@@ -100,8 +128,8 @@ class KernelDispatchTable<Kernels,
internal::KernelTypeList<Params...>,
internal::KernelTypeList<OptParams...>>
{
using TableType =
std::unordered_map<std::tuple<Params...>, Signature, TupleHasher>;
using TableType = std::unordered_map<std::tuple<Params...>,
Signature, KernelDispatchKeyHash<Params...>>;
TableType table;
/// @brief Call function @a f with arguments @a args (perfect forwaring).
+5 -6
View File
@@ -158,16 +158,15 @@ 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[UnsignIndex(i)];
int idof_lor = vdof_lor[absdof(i)];
i = dofmap_ho[off_ho + i1*n1 + i2*n2];
int s2 = i < 0 ? -1 : 1;
int idof_ho = vdof_ho[UnsignIndex(i)];
int idof_ho = vdof_ho[absdof(i)];
int s3 = idof_lor < 0 ? -1 : 1;
int s4 = idof_ho < 0 ? -1 : 1;
int s = s1*s2*s3*s4;
i = UnsignIndex(idof_ho);
perm_[UnsignIndex(idof_lor)] = s < 0 ? -1-UnsignIndex(i) :
UnsignIndex(i);
i = absdof(idof_ho);
perm_[absdof(idof_lor)] = s < 0 ? -1-absdof(i) : absdof(i);
}
}
};
@@ -233,7 +232,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(UnsignIndex(j));
int t_j = pfes_ho->GetLocalTDofNumber(absdof(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))
{
+2
View File
@@ -57,6 +57,8 @@ 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 };
+3 -9
View File
@@ -488,16 +488,10 @@ void ParBilinearForm::FormLinearSystem(
R.Mult(x, true_X);
FormSystemMatrix(ess_tdof_list, A);
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
{
Operator *op;
Operator::FormSystemOperator(ess_tdof_list, op);
return dynamic_cast<ConstrainedOperator*>(op);
}());
MFEM_ASSERT(A_constrained != nullptr, "");
ConstrainedOperator *A_constrained;
Operator::FormConstrainedSystemOperator(ess_tdof_list, A_constrained);
A_constrained->EliminateRHS(true_X, true_B);
delete A_constrained;
R.MultTranspose(true_B, b);
hybridization->ReduceRHS(true_B, B);
X.SetSize(B.Size());
+56 -49
View File
@@ -424,7 +424,7 @@ void ParFiniteElementSpace::GetGroupComm(
{
if (ind[l] < 0)
{
dofs[l] = m + FlipIndexSign(ind[l]);
dofs[l] = m + (-1-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 + FlipIndexSign(ind[l]);
dofs[l] = m + (-1-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 + FlipIndexSign(ind[l]);
dofs[l] = m + (-1-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[FlipIndexSign(dofs[i])] < 0)
if (ldof_sign[-1-dofs[i]] < 0)
{
dofs[i] = FlipIndexSign(dofs[i]);
dofs[i] = -1-dofs[i];
}
}
else
{
if (ldof_sign[dofs[i]] < 0)
{
dofs[i] = FlipIndexSign(dofs[i]);
dofs[i] = -1-dofs[i];
}
}
}
@@ -646,38 +646,39 @@ const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
auto itr = L2F.find(key);
if (itr != L2F.end())
{
return itr->second.get();
return itr->second;
}
else
{
std::unique_ptr<FaceRestriction> res;
FaceRestriction *res;
if (is_dg_space)
{
if (Conforming())
{
res.reset(new ParL2FaceRestriction(*this, f_ordering, type, m));
res = new ParL2FaceRestriction(*this, f_ordering, type, m);
}
else
{
res.reset(new ParNCL2FaceRestriction(*this, f_ordering, type, m));
res = new ParNCL2FaceRestriction(*this, f_ordering, type, m);
}
}
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
{
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
}
else
{
if (Conforming())
{
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
res = new ConformingFaceRestriction(*this, f_ordering, type);
}
else
{
res.reset(new ParNCH1FaceRestriction(*this, f_ordering, type));
res = new ParNCH1FaceRestriction(*this, f_ordering, type);
}
}
return L2F.emplace(key, std::move(res)).first->second.get();
L2F[key] = res;
return res;
}
}
@@ -699,8 +700,7 @@ void ParFiniteElementSpace::GetSharedEdgeDofs(
for (int i = 0; i < dofs.Size(); i++)
{
const int di = dofs[i];
dofs[i] = di >= 0 ? rdofs[di] :
FlipIndexSign(rdofs[FlipIndexSign(di)]);
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
}
}
}
@@ -724,8 +724,7 @@ void ParFiniteElementSpace::GetSharedTriangleDofs(
for (int i = 0; i < dofs.Size(); i++)
{
const int di = dofs[i];
dofs[i] = di >= 0 ? rdofs[di] :
FlipIndexSign(rdofs[FlipIndexSign(di)]);
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
}
}
}
@@ -749,8 +748,7 @@ void ParFiniteElementSpace::GetSharedQuadrilateralDofs(
for (int i = 0; i < dofs.Size(); i++)
{
const int di = dofs[i];
dofs[i] = (di >= 0) ? rdofs[di] :
FlipIndexSign(rdofs[FlipIndexSign(di)]);
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
}
}
}
@@ -1490,7 +1488,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
GetElementVDofs(my_elems[i], ldofs);
for (int j = 0; j < ldofs.Size(); j++)
{
int ldof = UnsignIndex(ldofs[j]);
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
if (ldof_marker[ldof] != fn)
{
@@ -1551,7 +1549,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
GetElementVDofs(my_elems[i], ldofs);
for (int j = 0; j < ldofs.Size(); j++)
{
int ldof = UnsignIndex(ldofs[j]);
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
if (ldof_marker[ldof] != fn)
{
@@ -1576,15 +1574,14 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
for (int i = 0; i < num_ldofs; i++)
{
int ldof = UnsignIndex(ldofs_fn[i]);
int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
ldof_marker[ldof] = i;
}
for ( ; j < j_end; j++)
{
const int ldof = UnsignIndex(send_J[j]);
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] :
FlipIndexSign(ldof_marker[ldof]));
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]);
}
}
@@ -1676,7 +1673,12 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
{
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
{
const int ldof = UnsignIndex(face_nbr_ldof.GetJ()[j]);
int ldof = face_nbr_ldof.GetJ()[j];
if (ldof < 0)
{
ldof = -1-ldof;
}
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
}
}
@@ -1720,7 +1722,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 = FlipIndexSign(el2);
el2 = -1 - 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);
@@ -1736,8 +1738,7 @@ 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] :
FlipIndexSign(vol_vdofs[FlipIndexSign(ldof)]);
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
}
}
@@ -2061,8 +2062,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
for (int j = 0; j < ne; j++)
{
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
FlipIndexSign(first + FlipIndexSign(ind[j]));
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
/* */ : (-1 - (first + (-1 - ind[j])));
}
}
else
@@ -2072,8 +2073,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]) :
FlipIndexSign(first + FlipIndexSign(ind[j]));
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
/* */ : (-1 - (first + (-1 - ind[j])));
}
}
}
@@ -2866,7 +2867,7 @@ void NeighborRowMessage::Encode(int rank)
if (ind && (edof = ind[edof]) < 0)
{
edof = FlipIndexSign(edof);
edof = -1 - edof;
s = -1;
}
@@ -3067,10 +3068,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 = UnsignIndex(edof);
edof = (edof < 0) ? -1 - edof : edof;
if (ind && (edof = ind[edof]) < 0)
{
edof = FlipIndexSign(edof);
edof = -1 - edof;
s *= -1.0;
}
@@ -3121,10 +3122,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 = UnsignIndex(edof);
edof = (edof < 0) ? -1 - edof : edof;
if (ind && (edof = ind[edof]) < 0)
{
edof = FlipIndexSign(edof);
edof = -1 - edof;
s *= -1.0;
}
@@ -4405,9 +4406,12 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
{
for (int j = 0; j < dofs.Size(); j++)
{
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
const int col = UnsignIndex(DofToVDof(old_dofs[j], vd,
old_ndofs));
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; }
i_diag[row] = col;
}
}
@@ -4432,7 +4436,9 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
{
for (int j = 0; j < dofs.Size(); j++)
{
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
int row = DofToVDof(dofs[j], vd);
if (row < 0) { row = -1 - row; }
if (i_diag[row] == i_diag[row+1]) // diag row empty?
{
i_offd[row] = old_dofs[j + vd * dofs.Size()];
@@ -4541,9 +4547,9 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
{
const Embedding &emb = dtrans.embeddings[k];
const int fine_rank = old_ranks[k];
const int coarse_rank = (emb.parent < 0) ? FlipIndexSign(emb.parent)
: old_pncmesh->ElementRank(emb.parent);
int fine_rank = old_ranks[k];
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
: old_pncmesh->ElementRank(emb.parent);
if (coarse_rank != MyRank && fine_rank == MyRank)
{
@@ -4631,8 +4637,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
{
if (!std::isfinite(lR(i, 0))) { continue; }
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
@@ -4681,7 +4687,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
{
if (!std::isfinite(lR(i, 0))) { continue; }
const int m = UnsignIndex(DofToVDof(dofs[i], vd));
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
-2
View File
@@ -483,8 +483,6 @@ 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); }
+19 -42
View File
@@ -39,10 +39,13 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, const GridFunction *gf,
{
const FiniteElementSpace *glob_fes = gf->FESpace();
// duplicate the FiniteElementCollection from 'gf'
fec_owned = FiniteElementCollection::New(glob_fes->FEColl()->Name());
fec.reset(FiniteElementCollection::New(glob_fes->FEColl()->Name()));
// create a local ParFiniteElementSpace from the global one:
fes = pfes = new ParFiniteElementSpace(pmesh, glob_fes, partitioning,
fec_owned);
owned_fes = std::make_shared<ParFiniteElementSpace>(pmesh, glob_fes,
partitioning, fec.get());
fes = owned_fes.get();
pfes = static_cast<ParFiniteElementSpace *>(fes);
SetSize(pfes->GetVSize());
if (partitioning)
@@ -76,10 +79,17 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, std::istream &input)
: GridFunction(pmesh, input)
{
// Convert the FiniteElementSpace, fes, to a ParFiniteElementSpace:
pfes = new ParFiniteElementSpace(pmesh, fec_owned, fes->GetVDim(),
fes->GetOrdering());
delete fes;
fes = pfes;
owned_fes = std::make_shared<ParFiniteElementSpace>(
pmesh, fec.get(), fes->GetVDim(), fes->GetOrdering());
fes = owned_fes.get();
pfes = static_cast<ParFiniteElementSpace*>(fes);
}
ParGridFunction& ParGridFunction::operator=(const ParGridFunction &rhs)
{
operator=((const GridFunction &)rhs);
pfes = rhs.pfes;
return *this;
}
void ParGridFunction::Update()
@@ -1206,7 +1216,7 @@ GridFunction ParGridFunction::GetSerialGridFunction(int save_rank,
pfes->GetVDim(),
pfes->GetOrdering());
GridFunction serial_gf = GetSerialGridFunction(save_rank, *serial_fes);
serial_gf.MakeOwner(serial_fec); // Also assumes ownership of serial_fes
serial_gf.MakeOwner(); // Also assumes ownership of serial_fes
return serial_gf;
}
@@ -1446,7 +1456,7 @@ std::unique_ptr<ParGridFunction> ParGridFunction::ProlongateToMaxOrder() const
PRefinementTransferOperator P(*pfes, *pfesMax);
P.Mult(*this, *xMax);
xMax->MakeOwner(fecMax);
xMax->MakeOwner();
return std::unique_ptr<ParGridFunction>(xMax);
}
@@ -1568,39 +1578,6 @@ 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
+4 -20
View File
@@ -70,6 +70,8 @@ public:
ParGridFunction(const ParGridFunction &orig)
: GridFunction(orig), pfes(orig.pfes) { }
ParGridFunction(ParGridFunction &&orig) = default;
ParGridFunction(ParFiniteElementSpace *pf) : GridFunction(pf), pfes(pf) { }
/// Same as above but specify the device memory type
@@ -114,14 +116,8 @@ public:
constructed. The new ParGridFunction assumes ownership of both. */
ParGridFunction(ParMesh *pmesh, std::istream &input);
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use ParFiniteElementSpace%s
that have the same size.
@note Defining this method overwrites the implicitly defined copy
assignment operator. */
ParGridFunction &operator=(const ParGridFunction &rhs)
{ return operator=((const Vector &)rhs); }
ParGridFunction &operator=(const ParGridFunction &rhs);
ParGridFunction &operator=(ParGridFunction &&orig) = default;
/// Assign constant values to the ParGridFunction data.
ParGridFunction &operator=(real_t value)
@@ -609,18 +605,6 @@ 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. */
+7
View File
@@ -994,6 +994,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
if ( face.IsConforming() )
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
@@ -1009,6 +1010,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
}
else // Non-conforming face
{
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
@@ -1026,6 +1028,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
}
else if (type==FaceType::Boundary && face.IsBoundary())
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
@@ -1043,6 +1046,10 @@ 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()
+6 -2
View File
@@ -326,7 +326,9 @@ 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. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillI(SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
@@ -362,7 +364,9 @@ 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. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillJAndData(const Vector &fea_data,
SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
+4 -1
View File
@@ -271,7 +271,10 @@ 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
values.MakeRef(GetMemory(), vdim*s_offset, vdim, sl_size);
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);
}
inline void QuadratureFunction::GetValues(
+1 -7
View File
@@ -50,13 +50,7 @@ QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Fallback(
int DIM, int SDIM, int D1D, int Q1D)
{
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(""); }
}
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
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)
+5 -54
View File
@@ -56,50 +56,6 @@ 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,
@@ -334,16 +290,11 @@ template<int DIM, int SDIM, int D1D, int Q1D>
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Kernel()
{
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("");
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(""); }
}
/// @endcond
+4 -4
View File
@@ -203,10 +203,10 @@ template<int DIM, QVectorLayout Q_LAYOUT,
QuadratureInterpolator::TensorEvalKernelType
QuadratureInterpolator::TensorEvalKernels::Kernel()
{
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("");
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(""); }
}
/// @endcond
+2 -9
View File
@@ -453,15 +453,8 @@ QuadratureInterpolator::TensorEvalHDivKernels::Kernel()
{
using namespace internal::quadrature_interpolator;
static_assert(DIM == 2 || DIM == 3, "only DIM=2 and DIM=3 are implemented!");
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!");
if (DIM == 2) { return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>; }
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
}
/// @endcond
+8 -8
View File
@@ -592,10 +592,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
QuadratureInterpolator::GradKernelType
QuadratureInterpolator::GradKernels::Kernel()
{
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("");
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(""); }
}
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 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("");
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
else { MFEM_ABORT(""); }
}
/// @endcond
+5 -6
View File
@@ -542,8 +542,7 @@ void QuadratureInterpolator::Mult(const Vector &e_vec,
}
MFEM_ASSERT(!(eval_flags & DETERMINANTS) || dim == vdim ||
(dim == 2 && vdim == 3) || (dim == 1 && vdim == 2) ||
(dim == 1 && vdim == 3), "Invalid dimensions for determinants.");
(dim == 2 && vdim == 3), "Invalid dimensions for determinants.");
MFEM_ASSERT(fespace->GetMesh()->GetNumGeometries(
fespace->GetMesh()->Dimension()) == 1,
"mixed meshes are not supported");
@@ -752,10 +751,10 @@ template <int DIM, int VDIM, int ND, int NQ>
EvalKernel QuadratureInterpolator::EvalKernels::Kernel()
{
using namespace internal::quadrature_interpolator;
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("");
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(""); }
}
template <int DIM>
+48 -122
View File
@@ -844,6 +844,8 @@ 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,
@@ -866,9 +868,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 = UnsignIndex(s_volume_dof);
const int volume_dof = absdof(s_volume_dof);
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
const int global_dof = UnsignIndex(s_global_dof);
const int global_dof = absdof(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];
@@ -895,10 +897,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 = UnsignIndex(s_volume_dof);
const int volume_dof = absdof(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 = UnsignIndex(s_global_dof);
const int global_dof = absdof(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;
@@ -1504,12 +1506,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)
{ }
@@ -1534,8 +1536,7 @@ 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 uniqueness of the ptMat to identify the
// transformation.
// Unfortunately we can't trust unicity of the ptMat to identify the transformation.
Key key(ptMat, face_key);
auto itr = interp_map.find(key);
if ( itr == interp_map.end() )
@@ -1582,27 +1583,17 @@ 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;
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;
}
const int orientation = face.element[1].orientation;
for (int i = 0; i < face_dofs; i++)
{
const int ni = (dof_map.Size()==0) ? i : dof_map[i];
@@ -1611,7 +1602,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_i, dof1d, li);
orientation, dof1d, li);
}
for (int j = 0; j < face_dofs; j++)
{
@@ -1620,7 +1611,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_j, dof1d, lj);
orientation, dof1d, lj);
}
const int nj = (dof_map.Size()==0) ? j : dof_map[j];
(*interpolator)(li,lj) = native_interpolator(ni,nj);
@@ -1685,7 +1676,7 @@ NCL2FaceRestriction::NCL2FaceRestriction(const FiniteElementSpace &fes,
const L2FaceValues m,
bool build)
: L2FaceRestriction(fes, f_ordering, type, m, false),
interpolations(fes.GetInterpolationManager(ordering, type))
interpolations(fes, f_ordering, type)
{
if (!build) { return; }
x_interp.UseDevice(true);
@@ -2211,6 +2202,14 @@ 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() )
@@ -2220,6 +2219,7 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
SetBoundaryDofsScatterIndices2(face,f_ind);
}
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
f_ind++;
}
}
@@ -2232,6 +2232,10 @@ 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()
@@ -2274,18 +2278,6 @@ 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_,
@@ -2296,54 +2288,25 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
nfaces(fes.GetNFbyType(type)),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
face_dofs(fes.GetTypicalTraceElement()->GetDof()),
face_dofs(nfaces > 0 ? fes.GetFaceElement(0)->GetDof() : 0),
nfdofs(face_dofs*nfaces),
ndofs(fes.GetNDofs()),
nsdofs(GetSharedVSize(fes))
ndofs(fes.GetNDofs())
{
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;
scatter_map.SetSize(nfdofs);
gather_map.SetSize(ndofs + nsdofs);
gather_map = -1;
Array<int> dofs;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
gather_map.SetSize(nfdofs);
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse()) { continue; }
if (f < mesh.GetNumFaces())
if (!face.IsOfFaceType(type)) { continue; }
for (int i = 0; i < face_dofs; ++i)
{
// 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
gather_map[i + face_idx*face_dofs] = face2dof.GetJ()[i + f*face_dofs];
}
++face_idx;
}
@@ -2351,19 +2314,13 @@ 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 = scatter_map.Read();
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
MFEM_ASSERT(face_nbr_data.Size() / vd == nsdofs, "");
const int *map = gather_map.Read();
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)
@@ -2371,8 +2328,7 @@ void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
const int j = map[i];
for (int c = 0; c < vd; ++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); }
d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
}
});
}
@@ -2387,39 +2343,15 @@ void L2InterfaceFaceRestriction::AddMultTranspose(
const int *map = gather_map.Read();
const auto d_x = Reshape(x.Read(), nd, vd, nf);
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i) { d_y[i] = 0.0; });
mfem::forall(nd*nf, [=] 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) += 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);
d_y(t?c:j, t?j:c) = d_x(i % nd, c, i / nd);
}
});
}
@@ -2429,11 +2361,6 @@ 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)
{
@@ -2455,7 +2382,6 @@ Vector GetLVectorFaceNbrData(
{
ParGridFunction gf(pfes, const_cast<Vector&>(x));
gf.ExchangeFaceNbrData();
x.SyncMemory(gf);
return std::move(gf.FaceNbrData());
}
}
+14 -26
View File
@@ -812,12 +812,13 @@ 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::unordered_map<Key, std::pair<int,const DenseMatrix*>, PairHasher>;
using Map = std::map<Key, std::pair<int,const DenseMatrix*>>;
Map interp_map; // The temporary map that stores the interpolators.
public:
/** @brief Constructor.
InterpolationManager() = delete;
/** @brief main constructor.
@param[in] fes The FiniteElementSpace on which this operates
@param[in] ordering Request a specific element ordering.
@@ -908,7 +909,7 @@ private:
class NCL2FaceRestriction : virtual public L2FaceRestriction
{
protected:
const InterpolationManager &interpolations;
InterpolationManager interpolations;
mutable Vector x_interp;
/** @brief Constructs an NCL2FaceRestriction, this is a specialization of a
@@ -995,7 +996,9 @@ 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. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillI(SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
@@ -1013,7 +1016,9 @@ 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. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillJAndData(const Vector &fea_data,
SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
@@ -1031,7 +1036,9 @@ 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. */
elements.
@warning This method is not implemented yet. */
void AddFaceMatricesToElementMatrices(const Vector &fea_data,
Vector &ea_data) const override;
@@ -1123,9 +1130,7 @@ 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.
@@ -1163,24 +1168,7 @@ 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
+4 -7
View File
@@ -3938,7 +3938,7 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
dim);
// Initial gradients.
surf_fit_grad = new GridFunction(fes_grad);
surf_fit_grad->MakeOwner(fec_grad);
surf_fit_grad->MakeOwner();
for (int d = 0; d < dim; d++)
{
ParGridFunction surf_fit_grad_comp(fes, surf_fit_grad->GetData()+d*s0.Size());
@@ -3956,7 +3956,7 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
dim*dim);
// Initial Hessians.
surf_fit_hess = new GridFunction(fes_hess);
surf_fit_hess->MakeOwner(fec_hess);
surf_fit_hess->MakeOwner();
int id = 0;
for (int d = 0; d < dim; d++)
{
@@ -4102,11 +4102,8 @@ 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.
if (parallel)
{
const int dof_i = pfes->DofToVDof(i, 0);
if (pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
}
const int dof_i = pfes->DofToVDof(i, 0);
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
#endif
dof_cnt++;
+12 -31
View File
@@ -333,12 +333,6 @@ 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;
@@ -837,17 +831,11 @@ 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);
@@ -899,17 +887,11 @@ 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);
@@ -919,6 +901,7 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
void L2ProjectionGridTransfer::L2ProjectionL2Space::Prolongate(
const Vector &x, Vector &y) const
{
if (fes_ho.GetNE() == 0) { return; }
if (use_ea)
@@ -977,13 +960,14 @@ 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;
@@ -1260,6 +1244,13 @@ 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;
@@ -1869,11 +1860,6 @@ 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(
@@ -1920,11 +1906,6 @@ 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(
-2
View File
@@ -18,7 +18,6 @@ list(APPEND SRCS
gecko.cpp
globals.cpp
hash.cpp
hash_util.cpp
isockstream.cpp
mem_manager.cpp
occa.cpp
@@ -47,7 +46,6 @@ list(APPEND HDRS
globals.hpp
zstr.hpp
hash.hpp
hash_util.hpp
isockstream.hpp
kdtree.hpp
mem_alloc.hpp
+1 -24
View File
@@ -114,22 +114,10 @@ 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; }
// 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.
}
Swap(src); // Swap does not use move assignment!
src.DeleteAll();
return *this;
}
@@ -263,9 +251,6 @@ 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.
@@ -1088,14 +1073,6 @@ 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)
-2
View File
@@ -44,7 +44,6 @@
#endif
#if !defined(MFEM_USE_CUDA_OR_HIP)
constexpr bool mfem_use_gpu = false;
#define MFEM_DEVICE
#define MFEM_HOST
#define MFEM_LAMBDA
@@ -53,7 +52,6 @@ constexpr bool mfem_use_gpu = false;
#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__)) || \
-2
View File
@@ -20,11 +20,9 @@
#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))
+40 -203
View File
@@ -295,12 +295,11 @@ using hip_threads_z =
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA) && defined(__CUDACC__)
template <typename DBODY>
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
void RajaCuWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS,true>>(RAJA::RangeSegment(0,N),
d_body);
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
@@ -363,18 +362,18 @@ struct RajaCuWrap;
template <>
struct RajaCuWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap1D(N, d_body);
RajaCuWrap1D<BLCK>(N, d_body);
}
};
template <>
struct RajaCuWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -385,7 +384,7 @@ struct RajaCuWrap<2>
template <>
struct RajaCuWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -396,12 +395,11 @@ struct RajaCuWrap<3>
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
template <typename DBODY>
template <const int BLOCKS = MFEM_HIP_BLOCKS, typename DBODY>
void RajaHipWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true>>(RAJA::RangeSegment(0,N),
d_body);
RAJA::forall<RAJA::hip_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
@@ -464,18 +462,18 @@ struct RajaHipWrap;
template <>
struct RajaHipWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap1D(N, d_body);
RajaHipWrap1D<BLCK>(N, d_body);
}
};
template <>
struct RajaHipWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -486,7 +484,7 @@ struct RajaHipWrap<2>
template <>
struct RajaHipWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -586,31 +584,12 @@ 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)
{
@@ -625,8 +604,6 @@ 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);
@@ -634,19 +611,6 @@ 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)
@@ -658,35 +622,24 @@ void CuWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
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)
{
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>
struct CuWrap;
template <int Dim, int MAX_THREADS_PER_BLOCK> struct CuWrap;
template <int MAX_THREADS_PER_BLOCK>
struct CuWrap<1, MAX_THREADS_PER_BLOCK>
template <>
struct CuWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
CuWrap1D<MFEM_CUDA_BLOCKS>(N, d_body);
CuWrap1D<BLCK>(N, d_body);
}
};
template <>
struct CuWrap<2, 0>
struct CuWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -694,22 +647,10 @@ struct CuWrap<2, 0>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct CuWrap<2, 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)
{
static_assert(MAX_THREADS_PER_BLOCK > 0);
CuWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
}
};
template <>
struct CuWrap<3, 0>
struct CuWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -717,17 +658,6 @@ struct CuWrap<3, 0>
}
};
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__)
@@ -750,31 +680,13 @@ 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 <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>
template <const int BLCK = MFEM_HIP_BLOCKS, typename DBODY>
void HipWrap1D(const int N, DBODY &&d_body)
{
if (N==0) { return; }
@@ -788,27 +700,12 @@ 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)
@@ -820,36 +717,24 @@ void HipWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(hipGetLastError());
}
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)
{
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>
struct HipWrap;
template <int Dim, int MAX_THREADS_PER_BLOCK> struct HipWrap;
template <int MAX_THREADS_PER_BLOCK>
struct HipWrap<1, MAX_THREADS_PER_BLOCK>
template <>
struct HipWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap1D<MFEM_HIP_BLOCKS>(N, d_body);
HipWrap1D<BLCK>(N, d_body);
}
};
template <>
struct HipWrap<2, 0>
struct HipWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -857,21 +742,10 @@ struct HipWrap<2, 0>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct HipWrap<2, 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)
{
HipWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
}
};
template <>
struct HipWrap<3, 0>
struct HipWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -879,24 +753,11 @@ struct HipWrap<3, 0>
}
};
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__)
///////////////////////////////////////////////////////////////////////////////
/// Forall host & device kernel dispatch
template <int DIM, int MAX_THREADS_PER_BLOCK = 0,
typename d_lambda, typename h_lambda>
/// The forall kernel body wrapper
template <const int DIM, 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,
@@ -929,7 +790,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, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
return CuWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -937,7 +798,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, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
return HipWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -966,9 +827,7 @@ backend_cpu:
for (int k = 0; k < N; k++) { h_body(k); }
}
///////////////////////////////////////////////////////////////////////////////
/// Forall host & device kernel wrappers
template <int DIM, typename lambda>
template <const 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)
@@ -976,16 +835,6 @@ 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); }
@@ -994,7 +843,7 @@ inline void forall(int Nx, int Ny, lambda &&body)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
mfem::forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
{
int j = idx / Nx;
int i = idx % Nx;
@@ -1030,7 +879,7 @@ inline void forall(int Nx, int Ny, int Nz, lambda &&body)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
mfem::forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
{
int i = idx % Nx;
int j = idx / Nx;
@@ -1078,12 +927,6 @@ 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)
{
@@ -1096,12 +939,6 @@ 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)
{
-4
View File
@@ -113,10 +113,6 @@ 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
+155
View File
@@ -80,4 +80,159 @@ 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
+70 -1
View File
@@ -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,6 +457,75 @@ 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
-172
View File
@@ -1,172 +0,0 @@
// 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];
}
}
-172
View File
@@ -1,172 +0,0 @@
// 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
-2
View File
@@ -20,11 +20,9 @@
#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))
+2 -19
View File
@@ -22,14 +22,6 @@
//#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>
@@ -767,14 +759,7 @@ private:
{
switch (mt)
{
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();
case MT::HOST_DEBUG: return new MmuHostMemorySpace();
#ifdef MFEM_USE_UMPIRE
case MT::HOST_UMPIRE:
return new UmpireHostMemorySpace(
@@ -803,9 +788,7 @@ private:
case MT::DEVICE_UMPIRE: return new NoDeviceMemorySpace();
case MT::DEVICE_UMPIRE_2: return new NoDeviceMemorySpace();
#endif
case MT::DEVICE_DEBUG:
if (GetEnv("MFEM_MMU_STD")) { return new StdDeviceMemorySpace(); }
return new MmuDeviceMemorySpace();
case MT::DEVICE_DEBUG: return new MmuDeviceMemorySpace();
case MT::DEVICE:
{
#if defined(MFEM_USE_CUDA)
-8
View File
@@ -200,10 +200,6 @@ 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++]);
@@ -282,10 +278,6 @@ 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);
+1 -9
View File
@@ -31,7 +31,7 @@ class Vector;
class OptionsParser
{
public:
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR , LONG };
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR };
private:
struct Option
@@ -98,14 +98,6 @@ 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)
-9
View File
@@ -82,15 +82,6 @@ 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. */
-179
View File
@@ -3634,25 +3634,12 @@ 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_)
{
@@ -3661,15 +3648,6 @@ 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_)
{
@@ -3678,14 +3656,6 @@ 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;
@@ -3708,13 +3678,6 @@ 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));
@@ -4210,20 +4173,12 @@ 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)
@@ -4235,7 +4190,6 @@ 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)
@@ -4260,54 +4214,21 @@ 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);
@@ -4423,20 +4344,6 @@ 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)
{
@@ -4492,69 +4399,26 @@ 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);
@@ -4712,37 +4576,16 @@ 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);
@@ -4839,21 +4682,6 @@ 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)
{
@@ -5342,13 +5170,6 @@ 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);
+7 -97
View File
@@ -1160,15 +1160,6 @@ 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(),
@@ -1178,28 +1169,20 @@ 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);
@@ -1207,15 +1190,12 @@ 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. */
@@ -1347,7 +1327,6 @@ public:
#endif
/// PCG solver in hypre
/// Defaults to (relative) tol=1e-6, atol=0, max_iter=1000
class HyprePCG : public HypreSolver
{
private:
@@ -1355,9 +1334,6 @@ private:
HypreSolver * precond;
/// Default PCG options
void SetDefaultOptions();
public:
HyprePCG(MPI_Comm comm);
@@ -1366,11 +1342,8 @@ 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);
@@ -1395,32 +1368,12 @@ 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; }
@@ -1438,8 +1391,7 @@ public:
virtual ~HyprePCG();
};
/// GMRES solver in hypre.
/// Defaults to k=50, (relative) tol=1e-6, atol=0, max_iter=100.
/// GMRES solver in hypre
class HypreGMRES : public HypreSolver
{
private:
@@ -1458,13 +1410,9 @@ 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);
@@ -1484,22 +1432,12 @@ 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; }
@@ -1517,8 +1455,7 @@ public:
virtual ~HypreGMRES();
};
/// Flexible GMRES solver in hypre.
/// Defaults to k=50, (relative) tol=1e-6, max_iter=100.
/// Flexible GMRES solver in hypre
class HypreFGMRES : public HypreSolver
{
private:
@@ -1537,11 +1474,8 @@ 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);
@@ -1561,22 +1495,12 @@ 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; }
@@ -1632,8 +1556,7 @@ public:
virtual ~HypreDiagScale() { }
};
/// The ParaSails preconditioner in hypre.
/// See SetDefaultOptions() for default solver options.
/// The ParaSails preconditioner in hypre
class HypreParaSails : public HypreSolver
{
private:
@@ -1762,14 +1685,10 @@ public:
/**
@brief Wrapper for Hypre's native parallel ILU preconditioner.
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:
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:
@code
mfem::HypreILU ilu();
@@ -1910,7 +1829,6 @@ 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)
@@ -1935,8 +1853,6 @@ 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)
@@ -2237,14 +2153,8 @@ 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);
-5
View File
@@ -1066,11 +1066,6 @@ 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])
+1 -12
View File
@@ -363,19 +363,14 @@ 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.IterRefine = SLU_DOUBLE;
// options.Trans = NOTRANS;
// options.IterRefine = SLU_DOUBLE;
// 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 || \
@@ -477,12 +472,6 @@ 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
+1 -6
View File
@@ -250,8 +250,7 @@ public:
work (default false) */
void SetSymmetricPattern(bool sym);
/** @brief Specify whether to perform parallel symbolic factorization
(default false)
/** @brief Specify whether to perform parallel symbolic factorization.
@note If true SuperLU will use superlu::PARMETIS for the Column
Permutation regardless of the setting */
void SetParSymbFact(bool par);
@@ -264,10 +263,6 @@ 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_;
+59 -64
View File
@@ -493,7 +493,8 @@ void Mesh::GetBdrElementTransformation(int i,
{
for (int j = 0; j < n; j++)
{
pm(k,j) = nodes(UnsignIndex(vdofs[n*k+j]));
int idx = vdofs[n*k+j];
pm(k,j) = nodes((idx<0)? -1-idx:idx);
}
}
ElTr->SetFE(bdr_el);
@@ -1355,7 +1356,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 = FlipIndexSign(e2);
face.element[1].index = -1 - e2;
face.element[1].orientation = inf2%64;
}
}
@@ -1378,7 +1379,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 = FlipIndexSign(e2);
face.element[1].index = -1 - e2;
face.element[1].orientation = inf2%64;
}
face.point_matrix = nc_faces_info[ncface].PointMatrix;
@@ -1404,7 +1405,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 = FlipIndexSign(e2);
face.element[1].index = -1 - e2;
face.element[1].orientation = inf2%64;
face.point_matrix = nc_faces_info[ncface].PointMatrix;
}
@@ -1437,7 +1438,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
break;
case FaceInfoTag::SharedConforming:
res.Elem1No = element[0].index;
res.Elem2No = FlipIndexSign(element[1].index);
res.Elem2No = -1 - 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;
@@ -1447,7 +1448,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
break;
case FaceInfoTag::SharedSlaveNonconforming:
res.Elem1No = element[0].index;
res.Elem2No = FlipIndexSign(element[1].index);
res.Elem2No = -1 - 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;
@@ -1455,7 +1456,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
break;
case FaceInfoTag::GhostSlave:
res.Elem1No = element[0].index;
res.Elem2No = FlipIndexSign(element[1].index);
res.Elem2No = -1 - 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;
@@ -4570,8 +4571,9 @@ Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
FiniteElementSpace *fes_copy =
new FiniteElementSpace(*fes, this, fec_copy);
Nodes = new GridFunction(fes_copy);
Nodes->MakeOwner(fec_copy);
*Nodes = *mesh.Nodes;
Nodes->MakeOwner();
// only copy underlying Vector data
*Nodes = static_cast<Vector &>(*mesh.Nodes);
own_nodes = 1;
}
else
@@ -4795,7 +4797,7 @@ Mesh::Mesh(const NURBSExtension& ext)
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
Ordering::byVDIM);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec);
Nodes->MakeOwner();
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
own_nodes = 1;
spaceDim = Nodes->VectorDim();
@@ -6513,7 +6515,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] = FlipIndexSign(edge_to_ukv[j]);
edge_to_ukv[j] = -1 - edge_to_ukv[j];
}
}
}
@@ -6550,6 +6552,9 @@ 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; };
@@ -6565,7 +6570,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 : FlipIndexSign(i);
edge_to_ukv[i] = (v[1] > v[0]) ? i : flipSign(i);
ukv_to_rpkv[i] = i;
}
return;
@@ -6615,14 +6620,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 = UnsignIndex(edge_to_pkv[edge]);
const int pkv_other = unSign(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 : FlipIndexSign(pkv);
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : flipSign(pkv);
}
}
}
@@ -6649,10 +6654,10 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
edge_to_ukv.SetSize(NumOfEdges);
for (int i = 0; i < NumOfEdges; i++)
{
const int pkv = UnsignIndex(edge_to_pkv[i]);
const int pkv = unSign(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) ? FlipIndexSign(ukv) : ukv;
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? flipSign(ukv) : ukv;
}
CorrectPatchTopoOrientations(edge_to_ukv);
@@ -6663,6 +6668,9 @@ 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;
@@ -6681,7 +6689,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
for (auto e : fe)
{
const int skv = edge_to_ukv[e];
if (skv == kv || FlipIndexSign(skv) == kv) { hasKV = true; }
if (skv == kv || flipSign(skv) == kv) { hasKV = true; }
}
if (hasKV)
{
@@ -6711,7 +6719,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;
@@ -6725,7 +6733,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) ? FlipIndexSign(ukvs[i]) : ukvs[i];
ukvs[i] = (oe[i] < 0) ? flipSign(ukvs[i]) : ukvs[i];
}
// Find the direction with this kv.
@@ -6733,19 +6741,12 @@ 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 || FlipIndexSign(skv) == kv)
if (skv == kv || flipSign(skv) == kv)
{
for (auto e : dir_edges[d])
if (!edgeSet[pe[e]])
{
thisDir = d;
}
thisDir = d;
}
}
if (thisDir == -1)
{
return false;
}
MFEM_VERIFY(thisDir >= 0, "");
// For this direction, find any edge already set. If no edge is set, we
// arbitrarily take the first.
@@ -6777,12 +6778,12 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
}
const int edge = pe[i];
if ((dim == 2 && ukvs[i] != FlipIndexSign(ukvs[ref_edge0])) ||
(dim == 3 && ukvs[i] == FlipIndexSign(ukvs[ref_edge0])))
if ((dim == 2 && ukvs[i] != flipSign(ukvs[ref_edge0])) ||
(dim == 3 && ukvs[i] == flipSign(ukvs[ref_edge0])))
{
// Flip the sign of this edge
MFEM_ASSERT(!edgeSet[edge], "");
edge_to_ukv[edge] = FlipIndexSign(edge_to_ukv[edge]);
MFEM_VERIFY(!edgeSet[edge], "");
edge_to_ukv[edge] = flipSign(edge_to_ukv[edge]);
}
edgeSet[edge] = true;
@@ -6827,11 +6828,10 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
int unsetDim = -1;
for (int d=0; d<dim; ++d) // Loop over dimensions.
{
for (auto e : dir_edges[d])
if (!edgeSet[pe[e]])
{
unsetDim = d;
}
if (!edgeSet[pe[dir_edges[d][0]]])
{
unsetDim = d;
}
}
if (unsetDim == -1)
@@ -6840,7 +6840,9 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
continue;
}
const int kv = UnsignIndex(edge_to_ukv[pe[dir_edges[unsetDim][0]]]);
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]]], "");
initKV = false;
@@ -6890,7 +6892,6 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
}
}
#ifdef MFEM_DEBUG
bool allSet = true;
for (auto eset : edgeSet)
{
@@ -6899,8 +6900,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
allSet = false;
}
}
MFEM_ASSERT(allSet && unset.size() == 0, "Some edge is not set");
#endif
MFEM_VERIFY(allSet && unset.size() == 0, "Some edge is not set");
delete face2elem;
}
@@ -6942,7 +6942,7 @@ void Mesh::LoadNonconformingPatchTopo(std::istream &input,
if (v[0] > v[1])
{
ukv = FlipIndexSign(ukv);
ukv = -1 - ukv;
}
edge_to_ukv[j] = ukv;
}
@@ -7070,7 +7070,7 @@ void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
const int old_space_dim = spaceDim;
SetNodalFESpace(nfes);
Nodes->MakeOwner(nfec);
Nodes->MakeOwner();
if (spaceDim != old_space_dim)
{
@@ -12439,7 +12439,11 @@ void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
for (int i = 0; i < NumOfEdges; i++)
{
edge_vertex->GetRow(i, vert);
const int ki = UnsignIndex(e_to_k[i]);
int ki = e_to_k[i];
if (ki < 0)
{
ki = -1 - ki;
}
if (vmap)
{
@@ -15496,7 +15500,7 @@ Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy, const bool closed)
fes2d = new FiniteElementSpace(mesh2d, fec2d, 2);
mesh2d->SetNodalFESpace(fes2d);
GridFunction *nodes2d = mesh2d->GetNodes();
nodes2d->MakeOwner(fec2d);
nodes2d->MakeOwner();
NodeExtrudeCoefficient ecoeff(2, ny, sy);
Vector lnodes;
@@ -15722,7 +15726,7 @@ Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz)
fes3d = new FiniteElementSpace(mesh3d, fec3d, 3);
mesh3d->SetNodalFESpace(fes3d);
GridFunction *nodes3d = mesh3d->GetNodes();
nodes3d->MakeOwner(fec3d);
nodes3d->MakeOwner();
NodeExtrudeCoefficient ecoeff(3, nz, sz);
Vector lnodes;
@@ -15748,18 +15752,9 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
{
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
// Closest int divisor to the cubit root, going down.
auto factor3 = [](int N)
auto factor = [&](int N)
{
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--)
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
{ if (N % i == 0) { return i; } }
return 1;
};
@@ -15783,22 +15778,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 = factor2(mpi_cnt);
mpi_x = factor(mpi_cnt);
mpi_y = mpi_cnt / mpi_x;
// Switch order for better balance.
el0_y = factor2(el0);
el0_y = factor(el0);
el0_x = el0 / el0_y;
}
else
{
mpi_x = factor3(mpi_cnt);
mpi_y = factor2(mpi_cnt / mpi_x);
mpi_x = factor(mpi_cnt);
mpi_y = factor(mpi_cnt / mpi_x);
mpi_z = mpi_cnt / mpi_x / mpi_y;
// Switch order for better balance.
el0_z = factor3(el0);
el0_y = factor2(el0 / el0_z);
el0_z = factor(el0);
el0_y = factor(el0 / el0_z);
el0_x = el0 / el0_y / el0_z;
}
+10 -15
View File
@@ -2078,13 +2078,12 @@ 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;
@@ -2094,13 +2093,8 @@ 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
@@ -2119,20 +2113,21 @@ 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;
}
/// Return true if the face is a boundary face.
/** @brief Return true if the face is a boundary face. */
bool IsBoundary() const
{
return topology == FaceTopology::Boundary;
}
/// Return true if the face is of the same type as @a type.
/// @brief Return true if the face is of the same type as @a type.
bool IsOfFaceType(FaceType type) const
{
switch (type)
@@ -2146,13 +2141,13 @@ public:
}
}
/// Return true if the face is a conforming face.
/// @brief Return true if the face is a conforming face.
bool IsConforming() const
{
return topology == FaceTopology::Conforming;
}
/// Return true if the face is a nonconforming fine face.
/// @brief Return true if the face is a nonconforming fine face.
bool IsNonconformingFine() const
{
return topology == FaceTopology::Nonconforming &&
@@ -2160,7 +2155,7 @@ public:
element[1].conformity == ElementConformity::Superset);
}
/// Return true if the face is a nonconforming coarse face.
/// @brief 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. */
@@ -2170,7 +2165,7 @@ public:
element[1].conformity == ElementConformity::Subset;
}
/// cast operator from FaceInformation to FaceInfo.
/// @brief cast operator from FaceInformation to FaceInfo.
operator Mesh::FaceInfo() const;
};
+4 -4
View File
@@ -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 static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
return CONTINUE + 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 ? static_cast<int>(CONTINUE) + static_cast<int>(DEREFINED) : NONE;
return derefs ? CONTINUE + DEREFINED : NONE;
}
@@ -290,7 +290,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it)
}
delete l2fes;
delete gf;
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
return CONTINUE + REFINED;
}
@@ -310,7 +310,7 @@ int Rebalancer::ApplyImpl(Mesh &mesh)
if (pmesh && pmesh->Nonconforming())
{
pmesh->Rebalance();
return static_cast<int>(CONTINUE) + static_cast<int>(REBALANCED);
return CONTINUE + REBALANCED;
}
#endif
return NONE;
+5 -5
View File
@@ -557,7 +557,7 @@ void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
fec = new QuadraticFECollection;
fes = new FiniteElementSpace(this, fec, spaceDim);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
Nodes->MakeOwner(); // Nodes will destroy 'fec' and 'fes'
own_nodes = 1;
// Map vtk points to edge/face/element dofs
@@ -607,7 +607,7 @@ void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
fec = new H1_FECollection(order,Dim,BasisType::ClosedUniform);
fes = new FiniteElementSpace(this, fec, spaceDim);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
Nodes->MakeOwner(); // Nodes will destroy 'fec' and 'fes'
own_nodes = 1;
Array<int> dofs;
@@ -1332,7 +1332,7 @@ void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
Ordering::byVDIM);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec);
Nodes->MakeOwner();
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
own_nodes = 1;
read_gf = 0;
@@ -2525,7 +2525,7 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
nfes = new FiniteElementSpace(this, nfec, spaceDim,
Ordering::byVDIM);
Nodes_gf.SetSpace(nfes);
Nodes_gf.MakeOwner(nfec);
Nodes_gf.MakeOwner();
int o = 0;
int el_order = 1;
@@ -4098,7 +4098,7 @@ static void FinalizeCubitSecondOrderMesh(Mesh &mesh,
FiniteElementSpace *fes = new FiniteElementSpace(&mesh, fec, Dim,
Ordering::byVDIM);
GridFunction *Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
Nodes->MakeOwner(); // Nodes will destroy 'fec' and 'fes'
mesh.SetNodalGridFunction(Nodes, true);
for (int block_id : unique_block_ids)
+2 -2
View File
@@ -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(FlipIndexSign(enode.edge_index),
Slave(-1 - 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(FlipIndexSign(eid.index), eid.element, eid.local, Geometry::TRIANGLE));
Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
int v0index = nodes[vn0].vert_index;
int v1index = nodes[vn1].vert_index;
+44 -40
View File
@@ -93,7 +93,7 @@ void NCNURBSExtension::GetMasterEdgeEntities(
}
else
{
const int auxEdge = FlipIndexSign(edge_i);
const int auxEdge = -1 - edge_i;
GetAuxEdgeVertices(auxEdge, sverts);
}
@@ -159,7 +159,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
{
if (edge < 0)
{
sideAuxEdges[s].Append(FlipIndexSign(edge));
sideAuxEdges[s].Append(-1 - edge);
}
else
{
@@ -456,7 +456,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
== afverts[j], "");
}
ori_f2 = FlipIndexSign(ori_f2);
ori_f2 = -1 - ori_f2;
}
else
{
@@ -468,7 +468,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
}
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
SlaveFaceInfo{FlipIndexSign(afid), ori_f2,
SlaveFaceInfo{-1 - 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{FlipIndexSign(auxFaceId), ori_f,
SlaveFaceInfo{-1 - 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] = FlipIndexSign(auxv2e.at(edge_v));
edges[i] = -1 - 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[FlipIndexSign(i) + j];
return b[-1 - i + j];
}
else if (i + j < a.Size())
{
@@ -679,7 +679,7 @@ void NCNURBSExtension::GetMasterEdgeDofs(bool dof, int me,
}
else // Auxiliary edge
{
GetAuxEdgeVertices(FlipIndexSign(slaveId), svert);
GetAuxEdgeVertices(-1 - 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 = UnsignIndex(ori);
const int oriAbs = ori < 0 ? -1 - ori : 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 = FlipIndexSign(slaveId);
const int auxFace = -1 - slaveId;
// Set slave face entity dimensions.
if (dof)
@@ -1171,7 +1171,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
}
else
{
const int auxEdge = FlipIndexSign(edge);
const int auxEdge = -1 - 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 = UnsignIndex(ori);
const int aori = ori < 0 ? -1 - ori : ori;
if (aori % 2 == 0)
{
e1 = 0;
@@ -1416,15 +1416,14 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
{
// Create a new auxiliary edge
auxv2e[childPair] = auxEdges.size();
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ? parentEdge :
FlipIndexSign(parentEdge),
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
parentEdge : -1 - parentEdge,
{childPair.first, childPair.second},
{newParentEdge ? 0 : prevKI, ks}});
}
}
const int childEdge = childPairTopo ? v2e[childPair] :
FlipIndexSign(auxv2e[childPair]);
const int childEdge = childPairTopo ? v2e[childPair] : -1 - 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
@@ -1461,15 +1460,14 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
// -1 denotes `ne` at endpoint
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
FlipIndexSign(parentEdge) :
parentEdge,
-1 - parentEdge : parentEdge,
{finalChildPair.first, finalChildPair.second},
{ks, -1}});
}
}
const int finalChildEdge = finalChildPairTopo ? v2e[finalChildPair] :
FlipIndexSign(auxv2e[finalChildPair]);
-1 - auxv2e[finalChildPair];
edgePairs.emplace_back(-1, -1, finalChildEdge, parentEdge);
}
@@ -1807,7 +1805,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
auxFaces.push_back(auxFace);
facePairs.emplace_back(
FacePairInfo{cv[0], parentFace,
SlaveFaceInfo{FlipIndexSign(auxv2f[childPair]),
SlaveFaceInfo{-1 - auxv2f[childPair],
0, {i0, j0}, {d0, d1}}});
}
}
@@ -2113,7 +2111,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
auxv2e[childPair] = auxEdges.size();
auxEdges.emplace_back(AuxiliaryEdge{pv0 < pv1 ?
parentEdge :
FlipIndexSign(parentEdge),
-1 - parentEdge,
{childPair.first, childPair.second},
{knotIndex0, knotIndex1}});
}
@@ -2133,8 +2131,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
const EdgePairInfo ep_e((e_idx == n_d - de) ? -1 : tv,
(e_idx == n_d - de) ? -1 : tvki,
FlipIndexSign(auxv2e[childPair]),
parentEdge);
-1 - auxv2e[childPair], parentEdge);
const bool unset = !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
if (unset)
@@ -2229,7 +2226,7 @@ void NCNURBSExtension::GetAuxFaceToPatchTable(Array2D<int> &auxface2patch)
if (s < 0)
{
// Auxiliary face.
const int aux = FlipIndexSign(s);
const int aux = -1 - s;
if (auxface2patch(aux, 0) >= 0)
{
if (auxface2patch(aux, 1) != -1) { consistent = false; }
@@ -2319,7 +2316,7 @@ void NCNURBSExtension::UpdateAuxiliaryKnotSpans(const Array<int> &rf)
for (auto auxEdge : auxEdges)
{
const int p = auxEdge.parent;
const int parent = UnsignIndex(p);
const int parent = p < 0 ? -1 - p : p;
const int kv = KnotInd(parent);
for (int i=0; i<2; ++i)
{
@@ -2385,8 +2382,14 @@ 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];
const int ki1 = ki1raw == -1 ? KnotVec(UnsignIndex(signedParentEdge))->GetNE()
: ki1raw;
int ki1 = ki1raw;
if (ki1raw == -1)
{
const bool rev = signedParentEdge < 0;
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
ki1 = KnotVec(parentEdge)->GetNE();
}
return ki1 - ki0;
}
@@ -2400,7 +2403,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[FlipIndexSign(se)].v[i]; }
for (int i=0; i<2; ++i) { sev[i] = auxEdges[-1 - se].v[i]; }
}
else
{
@@ -2456,7 +2459,7 @@ void NCNURBSExtension::GetMasterEdgePieceOffsets(int mid, Array<int> &os)
}
else
{
nes = AuxiliaryEdgeNE(FlipIndexSign(s));
nes = AuxiliaryEdgeNE(-1 - s);
}
os[i+1] = os[i] + nes;
@@ -2562,7 +2565,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
}
else // Aux edge
{
const int aux_edge = FlipIndexSign(s);
const int aux_edge = -1 - s;
if (auxef[aux_edge].Size() == 0)
{
auxef[aux_edge].SetSize(AuxiliaryEdgeNE(aux_edge));
@@ -2608,7 +2611,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
}
MFEM_VERIFY(consistent, "");
return partialChange ? FlipIndexSign(dirSet) : dirSet;
return partialChange ? -1 - dirSet : dirSet;
}
void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
@@ -2712,7 +2715,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
if (s < 0)
{
// Auxiliary face.
const int aux = FlipIndexSign(s);
const int aux = -1 - s;
for (int i=0; i<2; ++i)
{
const int patch = auxface2patch(aux, i);
@@ -2760,7 +2763,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
const int dirSetSigned = SetPatchFactors(p);
const bool partialChange = dirSetSigned < 0;
const int dirSet = UnsignIndex(dirSetSigned);
const int dirSet = partialChange ? -1 - dirSetSigned : dirSetSigned;
const bool changed = (patchState[p] != dirSet) || partialChange;
patchState[p] = dirSet;
@@ -2803,8 +2806,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 ?
FlipIndexSign(dirSetSigned_i) : dirSetSigned_i;
const int dirSet_i = partialChange_i ? -1 - dirSetSigned_i :
dirSetSigned_i;
const bool changed_i = (patchState[i] != dirSet_i) ||
partialChange_i;
patchState[p] = dirSet_i;
@@ -3024,7 +3027,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 = FlipIndexSign(s); } // Reversed order is encoded by the sign.
if (rev) { s = -1 - s; } // Reversed order is encoded by the sign.
return s;
}
@@ -3037,7 +3040,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
// see GetFaceOrientation.
bool Reorder2D(int ori, std::array<int, 2> &s0)
{
const int shift = UnsignIndex(ori);
const int shift = ori < 0 ? -1 - ori : 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}.
@@ -3061,7 +3064,7 @@ void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
int &n)
{
const bool rev = (signedShift < 0);
const int shift = UnsignIndex(signedShift);
const int shift = rev ? -1 - signedShift : signedShift;
MFEM_ASSERT(0 <= shift && shift < 4, "");
// We consider 8 cases for the possible values of rev and shift.
@@ -3133,7 +3136,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 = UnsignIndex(signedShift);
const int shift = rev ? -1 - signedShift : signedShift;
MFEM_ASSERT(0 <= shift && shift < 4, "");
// (0,0) <= (i,j) < (m,n) are old indices, and old vertex [shift] maps
@@ -3795,7 +3798,8 @@ void NCNURBSExtension::GenerateOffsets()
const int signedParentEdge = auxEdges[e].parent;
const int ki0 = auxEdges[e].ksi[0];
const int ki1raw = auxEdges[e].ksi[1];
const int parentEdge = UnsignIndex(signedParentEdge);
const bool rev = signedParentEdge < 0;
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
const int masterNE = KnotVec(parentEdge)->GetNE();
const int ki1 = ki1raw == -1 ? masterNE : ki1raw;
const int perEdgeCP = GetNCPperEdge(KnotVec(e));
+18 -31
View File
@@ -43,30 +43,13 @@ 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;
if (NCP == -1)
{
for (int i = 0 ; i < Order + 1; i++)
{
knot[i] = 0.0;
knot[i + Order + 1] = 1.0;
}
}
else
{
knot = -1.;
}
knot = -1.;
}
KnotVector::KnotVector(int order, const Vector &k)
@@ -3723,7 +3706,10 @@ bool NURBSExtension::CheckPatches()
for (int i = 0; i < edges.Size(); i++)
{
edges[i] = edge_to_ukv[edges[i]];
if (oedge[i] < 0) { edges[i] = FlipIndexSign(edges[i]); }
if (oedge[i] < 0)
{
edges[i] = -1 - edges[i];
}
}
// In 2d - opposite edges must be same knotvector with opposite sign.
@@ -3737,7 +3723,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] != FlipIndexSign(edges[2]) || edges[1] != FlipIndexSign(edges[3]))) ||
(edges[0] != -1 - edges[2] || edges[1] != -1 - edges[3])) ||
(dim == 3 &&
(edges[0] != edges[2] || edges[0] != edges[4] ||
@@ -3766,7 +3752,7 @@ void NURBSExtension::CheckBdrPatches()
edges[i] = edge_to_ukv[edges[i]];
if (oedge[i] < 0)
{
edges[i] = FlipIndexSign(edges[i]);
edges[i] = -1 - edges[i];
}
}
@@ -4763,13 +4749,14 @@ void NURBSExtension::GenerateBdrElementDofTable()
SetPatchToBdrElements();
int *dof = bel_dof->GetJ();
const int ndof = bel_dof->Size_of_connections();
int ndof = bel_dof->Size_of_connections();
for (int i = 0; i < ndof; i++)
{
const int idx = dof[i];
int idx = dof[i];
if (idx < 0)
{
dof[i] = -activeDof[FlipIndexSign(idx)];
dof[i] = -1 - (activeDof[-1-idx] - 1);
dof[i] = -activeDof[-1-idx];
}
else
{
@@ -4854,12 +4841,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 = FlipIndexSign(conn.to); }
if (s == -1) { conn.to = -1 -conn.to; }
bel_dof_list.Append(conn);
}
}
bel_to_patch[lbe] = b;
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
lbe++;
}
gbe++;
@@ -4932,14 +4919,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 = FlipIndexSign(conn.to); }
if (s == -1) { conn.to = -1 -conn.to; }
bel_dof_list.Append(conn);
}
}
}
bel_to_patch[lbe] = b;
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : FlipIndexSign(j);
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : (-1-j);
lbe++;
}
gbe++;
+10 -11
View File
@@ -74,13 +74,9 @@ public:
integers are read, for order and number of control points. */
KnotVector(std::istream &input);
/** @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 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 and knots @a knot.
If @a k has the correct number of repeated knots at the begin and end,
@@ -92,10 +88,12 @@ 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);
@@ -220,7 +218,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
conjunction with GetSpan and GetRefPoint.*/
conjuction 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).
@@ -1398,7 +1396,8 @@ inline const real_t &NURBSPatch::operator()(int i, int j, int k, int l) const
inline int NURBSExtension::KnotInd(int edge) const
{
return UnsignIndex(edge_to_ukv[edge]);
const int kv = edge_to_ukv[edge];
return kv >= 0 ? kv : -1 - kv;
}
inline int NURBSExtension::KnotSign(int edge) const
@@ -1428,7 +1427,7 @@ const
else
{
*okv = -oedge;
return knotVectors[FlipIndexSign(kv)];
return knotVectors[-1-kv];
}
}
+5 -7
View File
@@ -86,7 +86,7 @@ ParMesh::ParMesh(const ParMesh &pmesh, bool copy_nodes)
ParFiniteElementSpace *pfes_copy =
new ParFiniteElementSpace(*fes, *this, fec_copy);
Nodes = new ParGridFunction(pfes_copy);
Nodes->MakeOwner(fec_copy);
Nodes->MakeOwner();
*Nodes = *pmesh.Nodes;
own_nodes = 1;
}
@@ -286,7 +286,7 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, const int *partitioning_,
new ParFiniteElementSpace(this, nfec, glob_fes->GetVDim(),
glob_fes->GetOrdering());
Nodes = new ParGridFunction(pfes);
Nodes->MakeOwner(nfec); // Nodes will own nfec and pfes
Nodes->MakeOwner(); // Nodes will own nfec and pfes
}
own_nodes = 1;
@@ -2032,7 +2032,7 @@ void ParMesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
auto pnodes = new ParGridFunction(nfes);
GetNodes(*pnodes);
NewNodes(*pnodes, true);
Nodes->MakeOwner(nfec);
Nodes->MakeOwner();
}
void ParMesh::SetNodalFESpace(FiniteElementSpace *nfes)
@@ -2067,9 +2067,7 @@ void ParMesh::EnsureParNodes()
*new_nodes = *Nodes;
if (Nodes->OwnFEC())
{
new_nodes->MakeOwner(Nodes->OwnFEC());
Nodes->MakeOwner(NULL); // takes away ownership of 'fec' and 'fes'
delete Nodes->FESpace();
new_nodes->MakeOwner();
}
delete Nodes;
Nodes = new_nodes;
@@ -5523,7 +5521,7 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
spaceDim,
GetNodalFESpace()->GetOrdering());
serialmesh.SetNodalFESpace(fespace_serial);
serialmesh.GetNodes()->MakeOwner(fec_serial);
serialmesh.GetNodes()->MakeOwner();
// The serial mesh owns its Nodes and they, in turn, own fec_serial and
// fespace_serial.
}
+9 -8
View File
@@ -400,7 +400,7 @@ void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
}
else // special case: prism edge-face constraint
{
if (entity_owner[1][FlipIndexSign(si)] != MyRank)
if (entity_owner[1][-1-si] != MyRank)
{
master_flag |= 0x2;
}
@@ -571,10 +571,9 @@ void ParNCMesh::CalculatePMatrixGroups()
ranks.SetSize(0);
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
{
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
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
ranks.Append(groups[owner][0]);
}
ranks.Sort();
@@ -1182,7 +1181,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 = FlipIndexSign(fnbr_index[e[0]->index - NElements]);
fi.Elem2No = -1 - fnbr_index[e[0]->index - NElements];
if (Dim == 3)
{
@@ -1212,6 +1211,7 @@ 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 = FlipIndexSign(fnbr_index[fi.Elem2No - NElements]);
fi.Elem2No = -1 - fnbr_index[fi.Elem2No - NElements];
const DenseMatrix* pm = full_list.point_matrices[sf.geom][sf.matrix];
if (!sloc && Dim == 3)
@@ -1312,6 +1312,7 @@ 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));
}
@@ -2287,7 +2288,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 = FlipIndexSign(elements[coarse[i]].rank);
index = -1 - elements[coarse[i]].rank;
}
transforms.embeddings[i].parent = index;
}
-2
View File
@@ -31,8 +31,6 @@ 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)
+2 -2
View File
@@ -461,7 +461,7 @@ void Boris::EvaluateFieldsAtParticles()
// Interpolate E-field + B-field onto particles
if (E_gf)
{
E_finder.Interpolate(*E_gf, E, E.GetOrdering());
E_finder.Interpolate(*E_gf, E); // E.GetOrdering() used internally
}
else
{
@@ -469,7 +469,7 @@ void Boris::EvaluateFieldsAtParticles()
}
if (B_gf)
{
B_finder.Interpolate(*B_gf, B, B.GetOrdering());
B_finder.Interpolate(*B_gf, B); // B.GetOrdering() used internally
}
else
{
+8 -4
View File
@@ -421,18 +421,22 @@ 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());
finder.FindPoints(X(), X().GetOrdering());
finder.Interpolate(u_gf, U(), U().GetOrdering());
finder.Interpolate(u_gf, U());
Ordering::Reorder(U(), U().GetVDim(), u_gf.ParFESpace()->GetOrdering(),
U().GetOrdering());
finder.Interpolate(w_gf, W(), W().GetOrdering());
finder.Interpolate(w_gf, W());
Ordering::Reorder(W(), W().GetVDim(), w_gf.ParFESpace()->GetOrdering(),
W().GetOrdering());
}
void NavierParticles::DeactivateLostParticles(bool findpts)
{
if (findpts)
{
finder.FindPoints(X());
finder.FindPoints(X(), X().GetOrdering());
}
const Array<unsigned int> lost_idxs = finder.GetPointsNotFoundIndices();
+1 -1
View File
@@ -224,7 +224,7 @@ Mesh *skin_mesh(Mesh *mesh)
FiniteElementSpace *fes_copy =
new FiniteElementSpace(*fes, bmesh, fec_copy);
GridFunction *bdr_nodes = new GridFunction(fes_copy);
bdr_nodes->MakeOwner(fec_copy);
bdr_nodes->MakeOwner();
bmesh->NewNodes(*bdr_nodes, true);
-32
View File
@@ -1,32 +0,0 @@
# 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
-65
View File
@@ -1,65 +0,0 @@
# 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.
list(APPEND CHPT_SOURCES )
list(APPEND CHPT_HEADERS )
convert_filenames_to_full_paths(CHPT_SOURCES)
convert_filenames_to_full_paths(CHPT_HEADERS)
set(CHPT_COMMON_FILES
EXTRA_SOURCES ${CHPT_SOURCES}
EXTRA_HEADERS ${CHPT_HEADERS})
add_mfem_miniapp(mtop_test_scalar_chpt
MAIN tst_scalar_field.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_vector_chpt
MAIN tst_vector_field.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_fixedslot
MAIN tst_fixedslot_scalar_vector.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_segmentio
MAIN tst_segmentio_scalar_vector_mfem.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_file
MAIN tst_fileio_scalar_vector_mfem.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_revolve
MAIN revolve.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_dyn
MAIN tst_dynamic_chpt.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_rev_fixedslot
MAIN tst_revolve_chpt_fixedslot.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
@@ -1,309 +0,0 @@
#ifndef MFEM_DYNAMIC_CHECKPOINTING_HPP
#define MFEM_DYNAMIC_CHECKPOINTING_HPP
#include "mfem.hpp"
#include <map>
#include <memory>
#include <limits>
#include <vector>
#include <utility>
namespace mfem
{
template <typename Snapshot>
class InMemoryCheckpointStorage
{
public:
using Handle = int;
Handle InvalidHandle() const { return -1; }
bool IsValid(const Handle &h) const { return h >= 0; }
Handle Store(Snapshot &&snap)
{
Handle id = InvalidHandle();
if (!free_.empty())
{
id = free_.back();
free_.pop_back();
if (slots_[id])
{
// reuse existing allocation
*slots_[id] = std::move(snap);
}
else
{
slots_[id].reset(new Snapshot(std::move(snap)));
}
}
else
{
id = (Handle) slots_.size();
slots_.push_back(std::unique_ptr<Snapshot>(new Snapshot(std::move(snap))));
}
return id;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "InMemoryCheckpointStorage: invalid handle.");
MFEM_VERIFY(h < (Handle) slots_.size(), "InMemoryCheckpointStorage: handle out of range.");
MFEM_VERIFY(slots_[h].get() != nullptr, "InMemoryCheckpointStorage: empty slot.");
f(*slots_[h]);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < (Handle) slots_.size(), "InMemoryCheckpointStorage: handle out of range.");
slots_[h].reset();
free_.push_back(h);
h = InvalidHandle();
}
private:
std::vector<std::unique_ptr<Snapshot>> slots_;
std::vector<Handle> free_;
};
/**
* Dynamic checkpointing manager (WangMoinIaccarino 2009), with pluggable storage.
*
* Snapshot: checkpointed object (often the primal State itself).
* Storage : must provide:
* using Handle = ...
* Handle InvalidHandle() const;
* bool IsValid(const Handle&) const;
* Handle Store(Snapshot&&);
* template<class F> void Read(const Handle&, F&&) const; // calls f(const Snapshot&)
* void Erase(Handle&);
*/
template <typename Snapshot,
typename Storage = InMemoryCheckpointStorage<Snapshot>>
class DynamicCheckpointing
{
public:
using Step = long long;
using Handle = typename Storage::Handle;
struct Checkpoint
{
int level = 0;
Handle h; // InvalidHandle => placeholder
Checkpoint() = default;
Checkpoint(int lvl, const Handle &inv) : level(lvl), h(inv) {}
};
explicit DynamicCheckpointing(int s)
: s_(s),
owned_storage_(new Storage()),
storage_(owned_storage_.get())
{
MFEM_VERIFY(s_ > 0, "DynamicCheckpointing: s must be > 0.");
Reset();
}
DynamicCheckpointing(int s, Storage &external_storage)
: s_(s),
owned_storage_(nullptr),
storage_(&external_storage)
{
MFEM_VERIFY(s_ > 0, "DynamicCheckpointing: s must be > 0.");
Reset();
}
~DynamicCheckpointing() { ReleaseAll(); }
void Reset()
{
ReleaseAll();
cps_.emplace(Step(0), Checkpoint(InfLevel(), storage_->InvalidHandle()));
}
struct CheckpointInfo
{
Step step;
int level;
bool stored;
};
std::vector<CheckpointInfo> GetCheckpointInfo() const
{
std::vector<CheckpointInfo> out;
out.reserve(cps_.size());
for (const auto &kv : cps_)
{
out.push_back({kv.first, kv.second.level, storage_->IsValid(kv.second.h)});
}
return out;
}
Step GetMaxStep() const
{
MFEM_VERIFY(!cps_.empty(), "DynamicCheckpointing: checkpoint map is empty.");
return cps_.rbegin()->first;
}
template <typename State, typename PrimalStepFn, typename MakeSnapshotFn>
void ForwardStep(const Step i,
State &u_i_inout,
PrimalStepFn &&primal_step,
MakeSnapshotFn &&make_snapshot)
{
AllocateCheckpointForNextStep(i);
auto it = cps_.find(i);
if (it != cps_.end())
{
storage_->Erase(it->second.h);
Snapshot snap = make_snapshot(u_i_inout);
it->second.h = storage_->Store(std::move(snap));
}
primal_step(u_i_inout, i);
}
template <typename State,
typename AdjState,
typename PrimalStepFn,
typename AdjointStepFn,
typename MakeSnapshotFn,
typename RestoreSnapshotFn>
void BackwardStep(const Step i,
AdjState &q_ip1_inout,
State &u_work_inout,
PrimalStepFn &&primal_step,
AdjointStepFn &&adjoint_step,
MakeSnapshotFn &&make_snapshot,
RestoreSnapshotFn &&restore_snapshot)
{
// remove placeholder at i+1
const Step ph = i + 1;
auto it_ph = cps_.find(ph);
MFEM_VERIFY(it_ph != cps_.end(),
"DynamicCheckpointing: expected checkpoint at i+1 before BackwardStep.");
storage_->Erase(it_ph->second.h);
cps_.erase(it_ph);
MFEM_ASSERT(GetMaxStep() <= i,
"DynamicCheckpointing: found a checkpoint beyond current adjoint step.");
auto restore_from_handle = [&](Handle &h)
{
storage_->Read(h, [&](const Snapshot &snap)
{
restore_snapshot(snap, u_work_inout);
});
storage_->Erase(h); // retrieved => placeholder (Algorithm 4 semantics)
};
if (GetMaxStep() == i)
{
Handle h = TakeHandleMakePlaceholder(i);
restore_from_handle(h);
}
else
{
const Step k = GetMaxStep();
Handle hk = TakeHandleMakePlaceholder(k);
restore_from_handle(hk);
for (Step t = k; t < i; ++t)
{
ForwardStep(t, u_work_inout, primal_step, make_snapshot);
}
}
adjoint_step(q_ip1_inout, u_work_inout, i);
}
private:
int s_ = 0;
std::unique_ptr<Storage> owned_storage_;
Storage *storage_ = nullptr;
std::map<Step, Checkpoint> cps_;
static int InfLevel() { return std::numeric_limits<int>::max(); }
void ReleaseAll()
{
if (!storage_) { cps_.clear(); return; }
for (auto &kv : cps_) { storage_->Erase(kv.second.h); }
cps_.clear();
}
bool FindDispensableLargestStep(Step &out_step) const
{
int max_level_seen = std::numeric_limits<int>::min();
for (auto it = cps_.rbegin(); it != cps_.rend(); ++it)
{
const Step step = it->first;
const int lvl = it->second.level;
if (max_level_seen > lvl) { out_step = step; return true; }
max_level_seen = (lvl > max_level_seen) ? lvl : max_level_seen;
}
return false;
}
void AllocateCheckpointForNextStep(const Step i)
{
const Step new_step = i + 1;
MFEM_VERIFY(cps_.find(new_step) == cps_.end(),
"DynamicCheckpointing: checkpoint at i+1 already exists.");
const Handle inv = storage_->InvalidHandle();
// allow growth to s+1 entries (incl. placeholder)
if ((int)cps_.size() <= s_)
{
cps_.emplace(new_step, Checkpoint(0, inv));
return;
}
Step disp = -1;
if (FindDispensableLargestStep(disp))
{
auto it = cps_.find(disp);
MFEM_ASSERT(it != cps_.end(), "Internal error: dispensable checkpoint not found.");
storage_->Erase(it->second.h);
cps_.erase(it);
cps_.emplace(new_step, Checkpoint(0, inv));
return;
}
auto it_i = cps_.find(i);
MFEM_VERIFY(it_i != cps_.end(),
"DynamicCheckpointing: promotion expected checkpoint at step i but none found.");
MFEM_VERIFY(i != 0, "DynamicCheckpointing: attempted to remove step 0 checkpoint.");
const int l = it_i->second.level;
storage_->Erase(it_i->second.h);
cps_.erase(it_i);
cps_.emplace(new_step, Checkpoint(l + 1, inv));
}
Handle TakeHandleMakePlaceholder(const Step i)
{
auto it = cps_.find(i);
MFEM_VERIFY(it != cps_.end(),
"DynamicCheckpointing: TakeHandle requested a non-existent checkpoint.");
MFEM_VERIFY(storage_->IsValid(it->second.h),
"DynamicCheckpointing: TakeHandle requested a checkpoint with no snapshot.");
Handle h = std::move(it->second.h);
it->second.h = storage_->InvalidHandle();
return h;
}
};
} // namespace mfem
#endif // MFEM_DYNAMIC_CHECKPOINTING_HPP
@@ -1,291 +0,0 @@
#ifndef MFEM_FILE_CHECKPOINT_STORAGE_HPP
#define MFEM_FILE_CHECKPOINT_STORAGE_HPP
#include "mfem.hpp"
#include <cstdint>
#include <cstdio> // std::remove, std::rename
#include <cstring>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <type_traits>
#if __has_include(<filesystem>)
#include <filesystem>
namespace mfem_fs = std::filesystem;
#define MFEM_HAVE_FILESYSTEM 1
#else
#define MFEM_HAVE_FILESYSTEM 0
#endif
namespace mfem
{
/**
* @brief Default binary serializer for checkpoint snapshots.
*
* Contract:
* - static void Write(std::ostream&, const Snapshot&)
* - static Snapshot Read (std::istream&)
*
* Default implementation supports trivially-copyable POD types.
* Specialization for mfem::Vector is provided below.
*
* Portability note:
* This binary format is NOT portable across endianness or differing sizeof(real_t).
* If you need portability, provide a custom SnapshotIO (e.g., text, XDR, HDF5).
*/
template <typename Snapshot, typename Enable = void>
struct DefaultCheckpointBinaryIO
{
static void Write(std::ostream &, const Snapshot &)
{
static_assert(sizeof(Snapshot) == 0,
"DefaultCheckpointBinaryIO: no implementation for this Snapshot type. "
"Provide a custom SnapshotIO with Write/Read.");
}
static Snapshot Read(std::istream &)
{
static_assert(sizeof(Snapshot) == 0,
"DefaultCheckpointBinaryIO: no implementation for this Snapshot type. "
"Provide a custom SnapshotIO with Write/Read.");
return Snapshot();
}
};
// POD / trivially-copyable types (double, int, structs of POD, etc.)
template <typename Snapshot>
struct DefaultCheckpointBinaryIO<
Snapshot,
typename std::enable_if<std::is_trivially_copyable<Snapshot>::value>::type>
{
static void Write(std::ostream &os, const Snapshot &x)
{
os.write(reinterpret_cast<const char*>(&x), sizeof(Snapshot));
MFEM_VERIFY(os.good(), "DefaultCheckpointBinaryIO: failed to write POD snapshot.");
}
static Snapshot Read(std::istream &is)
{
Snapshot x;
is.read(reinterpret_cast<char*>(&x), sizeof(Snapshot));
MFEM_VERIFY(is.good(), "DefaultCheckpointBinaryIO: failed to read POD snapshot.");
return x;
}
};
// Specialization for mfem::Vector
template <>
struct DefaultCheckpointBinaryIO<mfem::Vector, void>
{
static void Write(std::ostream &os, const mfem::Vector &v)
{
const std::int64_t n = (std::int64_t) v.Size();
os.write(reinterpret_cast<const char*>(&n), sizeof(n));
MFEM_VERIFY(os.good(), "VectorBinaryIO: failed to write vector size.");
if (n > 0)
{
const mfem::real_t *data = v.GetData();
os.write(reinterpret_cast<const char*>(data),
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
MFEM_VERIFY(os.good(), "VectorBinaryIO: failed to write vector data.");
}
}
static mfem::Vector Read(std::istream &is)
{
std::int64_t n = 0;
is.read(reinterpret_cast<char*>(&n), sizeof(n));
MFEM_VERIFY(is.good(), "VectorBinaryIO: failed to read vector size.");
MFEM_VERIFY(n >= 0, "VectorBinaryIO: invalid negative vector size.");
mfem::Vector v((int)n);
if (n > 0)
{
mfem::real_t *data = v.GetData();
is.read(reinterpret_cast<char*>(data),
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
MFEM_VERIFY(is.good(), "VectorBinaryIO: failed to read vector data.");
}
return v;
}
};
/**
* @brief File-based checkpoint storage backend.
*
* Stores each snapshot in a separate file:
* <directory>/<prefix><id><extension>
*
* Handle is a monotonically-increasing integer id (reused if keep_files==false).
*
* Template parameters:
* - Snapshot : stored snapshot type
* - SnapshotIO : serializer with static Write/Read methods (see DefaultCheckpointBinaryIO)
*
* Threading:
* - Not thread-safe. Typical adjoint/checkpointing usage is single-threaded control flow.
*/
template <typename Snapshot,
typename SnapshotIO = DefaultCheckpointBinaryIO<Snapshot>>
class FileCheckpointStorage
{
public:
using Handle = std::int64_t;
/**
* @param directory directory where checkpoint files live (created if create_dir==true)
* @param prefix filename prefix (e.g. "ckpt_")
* @param extension filename extension (e.g. ".bin")
* @param create_dir create directory if missing (requires <filesystem>)
* @param keep_files if true, Erase() will NOT delete files (useful for debugging),
* and ids are NOT reused.
*/
FileCheckpointStorage(const std::string &directory,
const std::string &prefix = "ckpt_",
const std::string &extension = ".bin",
bool create_dir = true,
bool keep_files = false)
: dir_(directory),
prefix_(prefix),
ext_(extension),
keep_files_(keep_files)
{
MFEM_VERIFY(!dir_.empty(), "FileCheckpointStorage: directory must be non-empty.");
MFEM_VERIFY(!prefix_.empty(), "FileCheckpointStorage: prefix must be non-empty.");
MFEM_VERIFY(!ext_.empty(), "FileCheckpointStorage: extension must be non-empty.");
if (create_dir)
{
#if MFEM_HAVE_FILESYSTEM
std::error_code ec;
mfem_fs::create_directories(mfem_fs::path(dir_), ec);
MFEM_VERIFY(!ec, "FileCheckpointStorage: failed to create directory: " << dir_);
#else
MFEM_ABORT("FileCheckpointStorage: create_dir=true requires <filesystem> support.");
#endif
}
}
Handle InvalidHandle() const { return (Handle)-1; }
bool IsValid(const Handle &h) const { return h >= 0; }
/**
* @brief Store snapshot to file, return handle.
*
* Uses atomic-ish pattern:
* - write to "<path>.tmp"
* - rename to "<path>"
*/
Handle Store(Snapshot &&snap)
{
const Handle id = AllocateId_();
const std::string path = Path_(id);
const std::string tmp = path + ".tmp";
{
std::ofstream os(tmp, std::ios::binary | std::ios::trunc);
MFEM_VERIFY(os.is_open(), "FileCheckpointStorage: failed to open for write: " << tmp);
// Write payload
SnapshotIO::Write(os, snap);
MFEM_VERIFY(os.good(), "FileCheckpointStorage: write failed for: " << tmp);
}
// Rename tmp -> final
const int rc = std::rename(tmp.c_str(), path.c_str());
MFEM_VERIFY(rc == 0, "FileCheckpointStorage: rename failed: " << tmp << " -> " << path);
return id;
}
/**
* @brief Read snapshot from file and pass it to callback f(const Snapshot&).
*
* The reference passed to f is valid only during the call.
*/
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "FileCheckpointStorage: Read called with invalid handle.");
const std::string path = Path_(h);
std::ifstream is(path, std::ios::binary);
MFEM_VERIFY(is.is_open(), "FileCheckpointStorage: failed to open for read: " << path);
Snapshot snap = SnapshotIO::Read(is);
MFEM_VERIFY(is.good(), "FileCheckpointStorage: read failed for: " << path);
f(snap);
}
/**
* @brief Erase snapshot (delete file unless keep_files==true), set handle invalid.
*
* Id reuse policy:
* - if keep_files_ == false: deleted ids are reused to avoid creating huge numbers of files
* - if keep_files_ == true : ids are not reused (avoid overwriting old debug files)
*/
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
if (!keep_files_)
{
const std::string path = Path_(h);
const int rc = std::remove(path.c_str());
MFEM_VERIFY(rc == 0, "FileCheckpointStorage: failed to remove file: " << path);
free_.push_back(h);
}
h = InvalidHandle();
}
private:
std::string dir_;
std::string prefix_;
std::string ext_;
bool keep_files_ = false;
mutable Handle next_id_ = 0;
mutable std::vector<Handle> free_;
Handle AllocateId_()
{
if (!keep_files_ && !free_.empty())
{
const Handle id = free_.back();
free_.pop_back();
return id;
}
return next_id_++;
}
std::string Path_(Handle id) const
{
MFEM_VERIFY(IsValid(id), "FileCheckpointStorage: Path_ called with invalid id.");
std::ostringstream oss;
oss << prefix_ << std::setw(12) << std::setfill('0') << id << ext_;
#if MFEM_HAVE_FILESYSTEM
mfem_fs::path p = mfem_fs::path(dir_) / mfem_fs::path(oss.str());
return p.string();
#else
// Fallback: simple concatenation; assumes dir_ ends without trailing slash if needed.
return dir_ + "/" + oss.str();
#endif
}
};
} // namespace mfem
#endif // MFEM_FILE_CHECKPOINT_STORAGE_HPP
@@ -1,385 +0,0 @@
#ifndef MFEM_FIXED_SLOT_CHECKPOINT_STORAGE_HPP
#define MFEM_FIXED_SLOT_CHECKPOINT_STORAGE_HPP
#include "mfem.hpp"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include <fstream>
#include <limits>
#include <type_traits>
namespace mfem
{
// ------------------------------------------------------------
// Packer 1: trivially-copyable snapshots (memcpy)
// ------------------------------------------------------------
template <typename Snapshot>
class TrivialFixedPacker
{
public:
TrivialFixedPacker()
{
static_assert(std::is_trivially_copyable<Snapshot>::value,
"TrivialFixedPacker requires Snapshot to be trivially copyable.");
}
std::size_t SlotBytes() const { return sizeof(Snapshot); }
void Pack(const Snapshot &snap, void *dst) const
{
std::memcpy(dst, &snap, sizeof(Snapshot));
}
void Unpack(const void *src, Snapshot &out) const
{
std::memcpy(&out, src, sizeof(Snapshot));
}
};
// ------------------------------------------------------------
// Packer 2: mfem::Vector of fixed length n
// (Serialized size is fixed: n*sizeof(real_t))
// ------------------------------------------------------------
class FixedVectorPacker
{
public:
explicit FixedVectorPacker(int n) : n_(n)
{
MFEM_VERIFY(n_ > 0, "FixedVectorPacker: n must be > 0.");
}
std::size_t SlotBytes() const
{
return (std::size_t)n_ * sizeof(mfem::real_t);
}
void Pack(const mfem::Vector &v, void *dst) const
{
MFEM_VERIFY(v.Size() == n_, "FixedVectorPacker: vector size mismatch.");
std::memcpy(dst, v.GetData(), SlotBytes());
}
void Unpack(const void *src, mfem::Vector &out) const
{
out.SetSize(n_);
std::memcpy(out.GetData(), src, SlotBytes());
}
int Size() const { return n_; }
private:
int n_ = 0;
};
// ------------------------------------------------------------
// Fixed-slot MEMORY storage: one big RAM block
// ------------------------------------------------------------
template <typename Snapshot, typename Packer = TrivialFixedPacker<Snapshot>>
class FixedSlotMemoryCheckpointStorage
{
public:
using Handle = int;
FixedSlotMemoryCheckpointStorage(int max_slots, const Packer &packer = Packer())
: max_slots_(max_slots), packer_(packer)
{
MFEM_VERIFY(max_slots_ > 0, "FixedSlotMemoryCheckpointStorage: max_slots must be > 0.");
slot_bytes_ = packer_.SlotBytes();
MFEM_VERIFY(slot_bytes_ > 0, "FixedSlotMemoryCheckpointStorage: SlotBytes must be > 0.");
// Single contiguous block
bytes_.resize((std::size_t)max_slots_ * slot_bytes_);
in_use_.assign(max_slots_, 0);
free_.reserve(max_slots_);
for (int i = 0; i < max_slots_; ++i) { free_.push_back(i); }
}
Handle InvalidHandle() const { return -1; }
bool IsValid(const Handle &h) const { return h >= 0; }
int MaxSlots() const { return max_slots_; }
std::size_t SlotBytes() const { return slot_bytes_; }
Handle Store(Snapshot &&snap)
{
MFEM_VERIFY(!free_.empty(),
"FixedSlotMemoryCheckpointStorage: out of slots (increase max_slots).");
const int slot = free_.back();
free_.pop_back();
in_use_[slot] = 1;
void *dst = SlotPtr_(slot);
packer_.Pack(snap, dst);
return slot;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "FixedSlotMemoryCheckpointStorage: Read invalid handle.");
MFEM_VERIFY(h < max_slots_, "FixedSlotMemoryCheckpointStorage: Read handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotMemoryCheckpointStorage: Read from free slot.");
Snapshot tmp;
const void *src = SlotPtrConst_(h);
packer_.Unpack(src, tmp);
f(tmp);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < max_slots_, "FixedSlotMemoryCheckpointStorage: Erase handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotMemoryCheckpointStorage: double-free / invalid erase.");
in_use_[h] = 0;
free_.push_back(h);
h = InvalidHandle();
}
/// Optional: return all slots to the free list (does not zero memory).
void Reset()
{
free_.clear();
for (int i = 0; i < max_slots_; ++i) { in_use_[i] = 0; free_.push_back(i); }
}
private:
int max_slots_ = 0;
std::size_t slot_bytes_ = 0;
Packer packer_;
std::vector<unsigned char> bytes_; // single block
std::vector<unsigned char> in_use_;
std::vector<int> free_;
void *SlotPtr_(int slot)
{
return (void*)(&bytes_[(std::size_t)slot * slot_bytes_]);
}
const void *SlotPtrConst_(int slot) const
{
return (const void*)(&bytes_[(std::size_t)slot * slot_bytes_]);
}
};
// ------------------------------------------------------------
// Fixed-slot FILE storage: one single pre-sized file
// ------------------------------------------------------------
template <typename Snapshot, typename Packer = TrivialFixedPacker<Snapshot>>
class FixedSlotFileCheckpointStorage
{
public:
using Handle = int;
struct Header
{
char magic[8]; // "MFCKPTFS"
std::uint64_t version; // 1
std::uint64_t slot_bytes;
std::uint64_t max_slots;
std::uint64_t reserved[4]; // future use / padding
};
FixedSlotFileCheckpointStorage(const std::string &path,
int max_slots,
const Packer &packer = Packer(),
bool truncate = true,
bool flush_on_store = false)
: path_(path),
max_slots_(max_slots),
packer_(packer),
flush_on_store_(flush_on_store)
{
MFEM_VERIFY(!path_.empty(), "FixedSlotFileCheckpointStorage: empty file path.");
MFEM_VERIFY(max_slots_ > 0, "FixedSlotFileCheckpointStorage: max_slots must be > 0.");
slot_bytes_ = packer_.SlotBytes();
MFEM_VERIFY(slot_bytes_ > 0, "FixedSlotFileCheckpointStorage: SlotBytes must be > 0.");
Open_(truncate);
in_use_.assign(max_slots_, 0);
free_.reserve(max_slots_);
for (int i = 0; i < max_slots_; ++i) { free_.push_back(i); }
scratch_.resize(slot_bytes_);
}
~FixedSlotFileCheckpointStorage()
{
if (file_.is_open()) { file_.close(); }
}
Handle InvalidHandle() const { return -1; }
bool IsValid(const Handle &h) const { return h >= 0; }
int MaxSlots() const { return max_slots_; }
std::size_t SlotBytes() const { return slot_bytes_; }
const std::string &Path() const { return path_; }
Handle Store(Snapshot &&snap)
{
MFEM_VERIFY(!free_.empty(),
"FixedSlotFileCheckpointStorage: out of slots (increase max_slots).");
const int slot = free_.back();
free_.pop_back();
in_use_[slot] = 1;
// Pack into scratch buffer then write into fixed slot offset
packer_.Pack(snap, scratch_.data());
const std::uint64_t off = SlotOffset_(slot);
file_.seekp((std::streamoff)off, std::ios::beg);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: seekp failed.");
file_.write(reinterpret_cast<const char*>(scratch_.data()),
(std::streamsize)slot_bytes_);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: write failed.");
if (flush_on_store_) { file_.flush(); }
return slot;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "FixedSlotFileCheckpointStorage: Read invalid handle.");
MFEM_VERIFY(h < max_slots_, "FixedSlotFileCheckpointStorage: Read handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotFileCheckpointStorage: Read from free slot.");
const std::uint64_t off = SlotOffset_(h);
file_.seekg((std::streamoff)off, std::ios::beg);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: seekg failed.");
file_.read(reinterpret_cast<char*>(scratch_.data()),
(std::streamsize)slot_bytes_);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: read failed.");
Snapshot tmp;
packer_.Unpack(scratch_.data(), tmp);
f(tmp);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < max_slots_, "FixedSlotFileCheckpointStorage: Erase handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotFileCheckpointStorage: double-free / invalid erase.");
// No file deletion; just return slot to free list.
in_use_[h] = 0;
free_.push_back(h);
h = InvalidHandle();
}
/// Optional: return all slots to free list (file contents remain).
void Reset()
{
free_.clear();
for (int i = 0; i < max_slots_; ++i) { in_use_[i] = 0; free_.push_back(i); }
}
private:
std::string path_;
int max_slots_ = 0;
std::size_t slot_bytes_ = 0;
Packer packer_;
bool flush_on_store_ = false;
// mutable because Read() is const but needs to seek/read
mutable std::fstream file_;
mutable std::vector<unsigned char> scratch_;
std::vector<unsigned char> in_use_;
std::vector<int> free_;
static Header MakeHeader_(std::uint64_t slot_bytes, std::uint64_t max_slots)
{
Header h;
std::memset(&h, 0, sizeof(h));
h.magic[0] = 'M'; h.magic[1] = 'F'; h.magic[2] = 'C'; h.magic[3] = 'K';
h.magic[4] = 'P'; h.magic[5] = 'T'; h.magic[6] = 'F'; h.magic[7] = 'S';
h.version = 1;
h.slot_bytes = slot_bytes;
h.max_slots = max_slots;
return h;
}
void Open_(bool truncate)
{
const std::ios::openmode mode =
std::ios::binary | std::ios::in | std::ios::out | (truncate ? std::ios::trunc : (std::ios::openmode)0);
file_.open(path_.c_str(), mode);
MFEM_VERIFY(file_.is_open(), "FixedSlotFileCheckpointStorage: failed to open file.");
const Header expected = MakeHeader_((std::uint64_t)slot_bytes_, (std::uint64_t)max_slots_);
if (truncate)
{
// Write header
file_.seekp(0, std::ios::beg);
file_.write(reinterpret_cast<const char*>(&expected), sizeof(expected));
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: header write failed.");
// Pre-size file to: header + max_slots*slot_bytes
const std::uint64_t total = (std::uint64_t)sizeof(Header)
+ (std::uint64_t)max_slots_ * (std::uint64_t)slot_bytes_;
MFEM_VERIFY(total > 0, "FixedSlotFileCheckpointStorage: invalid total file size.");
file_.seekp((std::streamoff)(total - 1), std::ios::beg);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: seekp for resize failed.");
const char zero = 0;
file_.write(&zero, 1);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: resize write failed.");
file_.flush();
}
else
{
// Validate existing header
Header got;
file_.seekg(0, std::ios::beg);
file_.read(reinterpret_cast<char*>(&got), sizeof(got));
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: header read failed.");
MFEM_VERIFY(std::memcmp(got.magic, expected.magic, 8) == 0,
"FixedSlotFileCheckpointStorage: magic mismatch.");
MFEM_VERIFY(got.version == expected.version,
"FixedSlotFileCheckpointStorage: version mismatch.");
MFEM_VERIFY(got.slot_bytes == expected.slot_bytes,
"FixedSlotFileCheckpointStorage: slot_bytes mismatch.");
MFEM_VERIFY(got.max_slots == expected.max_slots,
"FixedSlotFileCheckpointStorage: max_slots mismatch.");
}
}
std::uint64_t SlotOffset_(int slot) const
{
return (std::uint64_t)sizeof(Header) + (std::uint64_t)slot * (std::uint64_t)slot_bytes_;
}
};
} // namespace mfem
#endif // MFEM_FIXED_SLOT_CHECKPOINT_STORAGE_HPP
-694
View File
@@ -1,694 +0,0 @@
#include "mfem.hpp"
#include <cstdint>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using mfem::out;
// -------------------------------
// Fixed-slot storage: Memory
// -------------------------------
class FixedSlotMemoryStorage
{
private:
int max_slots_ = 0;
size_t slot_bytes_ = 0;
std::vector<uint8_t> data_;
public:
FixedSlotMemoryStorage(int max_slots, size_t slot_bytes)
: max_slots_(max_slots), slot_bytes_(slot_bytes),
data_(size_t(max_slots)*slot_bytes, 0)
{
MFEM_VERIFY(max_slots_ > 0, "max_slots must be > 0");
MFEM_VERIFY(slot_bytes_ > 0, "slot_bytes must be > 0");
}
int MaxSlots() const { return max_slots_; }
size_t SlotBytes() const { return slot_bytes_; }
void Save(int slot, const uint8_t *src, size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch");
MFEM_VERIFY(src != nullptr, "Save: src is null");
std::memcpy(data_.data() + size_t(slot)*slot_bytes_, src, slot_bytes_);
}
void Load(int slot, uint8_t *dst, size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch");
MFEM_VERIFY(dst != nullptr, "Load: dst is null");
std::memcpy(dst, data_.data() + size_t(slot)*slot_bytes_, slot_bytes_);
}
};
// -------------------------------
// Fixed-slot storage: Single file
// -------------------------------
class FixedSlotFileStorage
{
private:
int max_slots_ = 0;
size_t slot_bytes_ = 0;
std::string filename_;
mutable std::fstream file_;
std::streamoff Offset(int slot) const
{
return std::streamoff(size_t(slot) * slot_bytes_);
}
public:
FixedSlotFileStorage(const std::string &filename,
int max_slots,
size_t slot_bytes)
: max_slots_(max_slots), slot_bytes_(slot_bytes), filename_(filename)
{
MFEM_VERIFY(max_slots_ > 0, "max_slots must be > 0");
MFEM_VERIFY(slot_bytes_ > 0, "slot_bytes must be > 0");
MFEM_VERIFY(!filename_.empty(), "filename must not be empty");
// Create/truncate file and size it.
{
std::ofstream ofs(filename_, std::ios::binary | std::ios::trunc);
MFEM_VERIFY(ofs.good(), "Failed to create checkpoint file");
const size_t total = size_t(max_slots_) * slot_bytes_;
if (total > 0)
{
ofs.seekp(std::streamoff(total - 1));
char zero = 0;
ofs.write(&zero, 1);
}
}
file_.open(filename_, std::ios::binary | std::ios::in | std::ios::out);
MFEM_VERIFY(file_.good(), "Failed to open checkpoint file");
}
~FixedSlotFileStorage()
{
if (file_.is_open()) { file_.close(); }
}
int MaxSlots() const { return max_slots_; }
size_t SlotBytes() const { return slot_bytes_; }
void Save(int slot, const uint8_t *src, size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch");
MFEM_VERIFY(src != nullptr, "Save: src is null");
file_.seekp(Offset(slot));
MFEM_VERIFY(file_.good(), "Save: seekp failed");
file_.write(reinterpret_cast<const char*>(src), std::streamsize(slot_bytes_));
MFEM_VERIFY(file_.good(), "Save: write failed");
file_.flush();
}
void Load(int slot, uint8_t *dst, size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch");
MFEM_VERIFY(dst != nullptr, "Load: dst is null");
file_.seekg(Offset(slot));
MFEM_VERIFY(file_.good(), "Load: seekg failed");
file_.read(reinterpret_cast<char*>(dst), std::streamsize(slot_bytes_));
MFEM_VERIFY(file_.good(), "Load: read failed");
}
};
// -------------------------------
// REVOLVE controller (actions)
// (Transcribed from revolve.c used in ADOL-C; Algorithm 799.)
// -------------------------------
enum class RevolveAction
{
advance,
takeshot,
restore,
firsturn,
youturn,
terminate
};
class RevolveController
{
private:
int snaps_ = 0;
int check_ = -1;
int capo_ = 0;
int fine_ = 0;
int turn_ = 0;
int oldfine_ = 0;
int oldsnaps_ = 0;
// Stack of checkpoint times, indexed by 'check_'.
std::vector<int> ch_;
public:
RevolveController() = default;
RevolveController(int snaps, int capo0, int fine0)
: snaps_(snaps), check_(-1), capo_(capo0), fine_(fine0),
turn_(0), oldfine_(fine0), oldsnaps_(snaps), ch_(snaps, 0)
{
MFEM_VERIFY(snaps_ > 0, "REVOLVE snaps must be > 0");
MFEM_VERIFY(capo_ <= fine_, "REVOLVE: capo must be <= fine");
// Match revolve.c initialization behavior.
if (check_ == -1 && capo_ < fine_)
{
turn_ = 0;
ch_[0] = capo_ - 1;
}
}
int Snaps() const { return snaps_; }
int Check() const { return check_; }
int Capo() const { return capo_; }
int Fine() const { return fine_; }
const std::vector<int>& CheckpointTimes() const { return ch_; }
RevolveAction Next()
{
MFEM_VERIFY(!(check_ < -1), "REVOLVE: check < -1");
MFEM_VERIFY(!(capo_ > fine_), "REVOLVE: capo > fine");
if ((check_ == -1) && (capo_ < fine_))
{
turn_ = 0;
ch_[0] = capo_ - 1;
}
const int diff = fine_ - capo_;
switch (diff)
{
case 0:
{
// Terminate or restore to next checkpoint on the stack.
if (check_ == -1 || capo_ == ch_[0])
{
check_ -= 1; // mirror revolve.c behavior
return RevolveAction::terminate;
}
else
{
capo_ = ch_[check_];
oldfine_ = fine_;
return RevolveAction::restore;
}
}
case 1:
{
// One adjoint step available.
fine_ -= 1;
if (check_ >= 0 && ch_[check_] == capo_) { check_ -= 1; }
if (turn_ == 0)
{
turn_ = 1;
oldfine_ = fine_;
return RevolveAction::firsturn;
}
else
{
oldfine_ = fine_;
return RevolveAction::youturn;
}
}
default:
{
// diff > 1
if (check_ == -1 || ch_[check_] != capo_)
{
// Take a new checkpoint at current capo.
check_ += 1;
MFEM_VERIFY(check_ + 1 <= snaps_, "REVOLVE: exceeded snaps");
ch_[check_] = capo_;
oldfine_ = fine_;
return RevolveAction::takeshot;
}
else
{
// Advance capo forward within (capo, fine).
// This follows the binomial logic in revolve.c.
MFEM_VERIFY(!((oldfine_ < fine_) && (snaps_ == check_ + 1)),
"REVOLVE: fine increased unexpectedly with full stack");
const int oldcapo = capo_;
const int ds = snaps_ - check_;
MFEM_VERIFY(ds >= 1, "REVOLVE: ds < 1");
int reps = 0;
long long range = 1;
while (range < (fine_ - capo_))
{
reps += 1;
// range = range*(reps+ds)/reps (integer arithmetic)
range = range * (reps + ds) / reps;
}
MFEM_VERIFY(reps >= 1, "REVOLVE: reps < 1");
// Binomial helper values (integer)
const long long bino1 = range * reps / (ds + reps);
const long long bino2 = (ds > 1) ? (bino1 * ds / (ds + reps - 1)) : 1;
const long long bino3 =
(ds == 1) ? 0 :
(ds > 2) ? (bino2 * (ds - 1) / (ds + reps - 2)) : 1;
const long long bino4 = bino2 * (reps - 1) / ds;
const long long bino5 =
(ds < 3) ? 0 :
(ds > 3) ? (bino3 * (ds - 2) / reps) : 1;
// Kowarz "new version": keep l^ as small as possible
const long long bino6 = bino1 * ds / reps;
const long long gap = fine_ - capo_;
if (gap <= bino1 + bino3)
{
capo_ += int(bino4);
}
else if (gap < bino1 + bino2)
{
capo_ = fine_ - int(bino2 + bino3);
}
else if (gap <= bino1 + bino2 + bino5)
{
capo_ += int(bino1 - bino3);
}
else
{
capo_ = fine_ - int(bino6);
}
if (capo_ == oldcapo) { capo_ = oldcapo + 1; }
oldfine_ = fine_;
return RevolveAction::advance;
}
}
}
}
};
// -------------------------------
// Fixed-step REVOLVE checkpointing
// -------------------------------
template <typename StorageT>
class FixedStepRevolveCheckpointing
{
public:
struct Shot
{
int time = 0; // state index
int slot = 0; // checkpoint slot index
};
private:
int num_steps_ = 0; // total number of primal steps (0..num_steps)
int num_checkpoints_ = 0; // REVOLVE "snaps"
size_t snapshot_bytes_ = 0;
StorageT *storage_ = nullptr;
std::vector<Shot> forward_shots_;
int forward_shot_cursor_ = 0;
// Controller state at the beginning of the reverse sweep (pre-firsturn).
RevolveController ctrl_init_;
RevolveController ctrl_;
// Two scratch buffers for (de)serialization.
std::vector<uint8_t> io_buf_;
std::vector<uint8_t> prefinal_buf_;
bool prefinal_valid_ = false;
// Reverse sweep bookkeeping.
bool reverse_started_ = false;
int u_work_time_ = -1;
public:
FixedStepRevolveCheckpointing(int num_steps,
int num_checkpoints,
size_t snapshot_bytes,
StorageT &storage)
: num_steps_(num_steps),
num_checkpoints_(num_checkpoints),
snapshot_bytes_(snapshot_bytes),
storage_(&storage),
io_buf_(snapshot_bytes, 0),
prefinal_buf_(snapshot_bytes, 0)
{
MFEM_VERIFY(num_steps_ >= 0, "num_steps must be >= 0");
MFEM_VERIFY(num_checkpoints_ > 0, "num_checkpoints must be > 0");
MFEM_VERIFY(snapshot_bytes_ > 0, "snapshot_bytes must be > 0");
MFEM_VERIFY(storage_->MaxSlots() == num_checkpoints_,
"Storage MaxSlots() must match num_checkpoints");
MFEM_VERIFY(storage_->SlotBytes() == snapshot_bytes_,
"Storage SlotBytes() must match snapshot_bytes");
BuildForwardPlanAndInitialControllerState();
Reset();
}
void Reset()
{
forward_shot_cursor_ = 0;
prefinal_valid_ = false;
reverse_started_ = false;
u_work_time_ = -1;
ctrl_ = ctrl_init_;
}
int NumSteps() const { return num_steps_; }
int NumCheckpoints() const { return num_checkpoints_; }
size_t SnapshotBytes() const { return snapshot_bytes_; }
// ForwardStep: called for i=0..num_steps-1
template <typename State, typename PrimalStep, typename MakeSnapshot>
void ForwardStep(int i,
State &u,
PrimalStep &&primal_step,
MakeSnapshot &&make_snapshot)
{
MFEM_VERIFY(0 <= i && i < num_steps_, "ForwardStep: i out of range");
// Take any planned shots at time i (before advancing).
while (forward_shot_cursor_ < (int)forward_shots_.size() &&
forward_shots_[forward_shot_cursor_].time == i)
{
const int slot = forward_shots_[forward_shot_cursor_].slot;
make_snapshot(u, io_buf_.data(), snapshot_bytes_);
storage_->Save(slot, io_buf_.data(), snapshot_bytes_);
forward_shot_cursor_++;
}
// Cache u_{num_steps-1} so reverse can start there (REVOLVE expects capo=num_steps-1).
if (i == num_steps_ - 1)
{
make_snapshot(u, prefinal_buf_.data(), snapshot_bytes_);
prefinal_valid_ = true;
}
// Advance one step.
primal_step(i, u);
}
// BackwardStep: called for i=num_steps-1..0
template <typename State, typename AdjointState,
typename PrimalStep, typename AdjointStep,
typename MakeSnapshot, typename RestoreSnapshot>
void BackwardStep(int i,
AdjointState &lambda,
State &u_work,
PrimalStep &&primal_step,
AdjointStep &&adjoint_step,
MakeSnapshot &&make_snapshot,
RestoreSnapshot &&restore_snapshot)
{
MFEM_VERIFY(0 <= i && i < num_steps_, "BackwardStep: i out of range");
if (!reverse_started_)
{
MFEM_VERIFY(prefinal_valid_ || num_steps_ == 0,
"Reverse started but prefinal state was not captured. "
"Did you run the forward loop through i=num_steps-1?");
ctrl_ = ctrl_init_;
reverse_started_ = true;
if (num_steps_ > 0)
{
// Restore u_{num_steps-1} into u_work.
restore_snapshot(u_work, prefinal_buf_.data(), snapshot_bytes_);
u_work_time_ = num_steps_ - 1;
}
}
// Execute controller actions until we perform exactly one adjoint step.
for (;;)
{
RevolveAction act = ctrl_.Next();
switch (act)
{
case RevolveAction::takeshot:
{
const int slot = ctrl_.Check();
MFEM_VERIFY(u_work_time_ == ctrl_.Capo(),
"takeshot: u_work_time must equal capo");
make_snapshot(u_work, io_buf_.data(), snapshot_bytes_);
storage_->Save(slot, io_buf_.data(), snapshot_bytes_);
break;
}
case RevolveAction::restore:
{
const int slot = ctrl_.Check();
storage_->Load(slot, io_buf_.data(), snapshot_bytes_);
restore_snapshot(u_work, io_buf_.data(), snapshot_bytes_);
u_work_time_ = ctrl_.Capo();
break;
}
case RevolveAction::advance:
{
const int target = ctrl_.Capo();
MFEM_VERIFY(u_work_time_ >= 0, "advance: u_work_time not initialized");
MFEM_VERIFY(target >= u_work_time_, "advance: target < current time");
for (int t = u_work_time_; t < target; ++t)
{
primal_step(t, u_work);
}
u_work_time_ = target;
break;
}
case RevolveAction::firsturn:
case RevolveAction::youturn:
{
// After firsturn/youturn, ctrl_.Fine() has been decremented and equals ctrl_.Capo().
const int step = ctrl_.Fine();
MFEM_VERIFY(step == ctrl_.Capo(), "youturn: fine != capo");
MFEM_VERIFY(step == u_work_time_, "youturn: u_work_time != step");
MFEM_VERIFY(step == i, "BackwardStep called with i that doesn't match REVOLVE schedule");
// One adjoint step.
adjoint_step(step, u_work, lambda);
return;
}
case RevolveAction::terminate:
{
MFEM_ABORT("REVOLVE terminated early: BackwardStep called after completion?");
break;
}
}
}
}
private:
void BuildForwardPlanAndInitialControllerState()
{
forward_shots_.clear();
RevolveController sim(num_checkpoints_, /*capo=*/0, /*fine=*/num_steps_);
// Forward-plan phase: only TAKESHOT/ADVANCE should occur while fine-capo>1.
while (sim.Fine() - sim.Capo() > 1)
{
RevolveAction a = sim.Next();
if (a == RevolveAction::takeshot)
{
forward_shots_.push_back({sim.Capo(), sim.Check()});
}
else if (a == RevolveAction::advance)
{
// nothing to record; capo moved forward inside sim
}
else
{
MFEM_ABORT("Unexpected REVOLVE action during forward planning phase");
}
}
// At this point, sim is in the pre-firsturn state (capo = num_steps-1, fine = num_steps)
// for num_steps>=1. For num_steps<=1, fine-capo<=1 from the start.
ctrl_init_ = sim;
}
};
// ============================================================
// Mini example 1: scalar state (double)
// u_{n+1} = factor * u_n
// J = 0.5 (u_N - target)^2 => lambda_N = (u_N - target)
// lambda_n = factor * lambda_{n+1}
// ============================================================
static void RunScalarExample(int Nsteps, int Ncheck, double factor)
{
out << "\n--- Scalar REVOLVE example ---\n";
out << "Nsteps=" << Nsteps << ", Ncheckpoints=" << Ncheck << ", factor=" << factor << "\n";
const size_t snap_bytes = sizeof(double);
FixedSlotMemoryStorage storage(Ncheck, snap_bytes);
FixedStepRevolveCheckpointing<FixedSlotMemoryStorage> ckpt(Nsteps, Ncheck, snap_bytes, storage);
auto make_snapshot = [](const double &u, uint8_t *outb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double), "scalar snapshot size mismatch");
std::memcpy(outb, &u, sizeof(double));
};
auto restore_snapshot = [](double &u, const uint8_t *inb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double), "scalar snapshot size mismatch");
std::memcpy(&u, inb, sizeof(double));
};
auto primal_step = [factor](int /*step*/, double &u)
{
u *= factor;
};
auto adjoint_step = [factor](int /*step*/, const double & /*u_step*/, double &lambda)
{
lambda *= factor;
};
const double target = 2.0;
// Forward
double u = 1.0;
for (int i = 0; i < Nsteps; ++i)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
}
const double uN = u;
// Init adjoint at final state
double lambda = (uN - target);
// Reverse
double u_work = 0.0; // will be overwritten by restore_snapshot
for (int i = Nsteps - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
}
// Analytic lambda_0 = factor^Nsteps * (uN - target)
double factorN = 1.0;
for (int k = 0; k < Nsteps; ++k) { factorN *= factor; }
const double lambda0_exact = factorN * (uN - target);
out << "uN = " << uN << "\n";
out << "lambda0 (computed) = " << lambda << "\n";
out << "lambda0 (exact) = " << lambda0_exact << "\n";
}
// ============================================================
// Mini example 2: mfem::Vector state
// u_{n+1} = factor * u_n (elementwise scalar multiply)
// J = 0.5 ||u_N - target||^2 => lambda_N = (u_N - target)
// lambda_n = factor * lambda_{n+1}
// ============================================================
static void RunVectorExample(int Nsteps, int Ncheck, int dim, double factor)
{
out << "\n--- mfem::Vector REVOLVE example ---\n";
out << "Nsteps=" << Nsteps << ", Ncheckpoints=" << Ncheck
<< ", dim=" << dim << ", factor=" << factor << "\n";
MFEM_VERIFY(dim > 0, "dim must be > 0");
const size_t snap_bytes = sizeof(double) * size_t(dim);
FixedSlotMemoryStorage storage(Ncheck, snap_bytes);
FixedStepRevolveCheckpointing<FixedSlotMemoryStorage> ckpt(Nsteps, Ncheck, snap_bytes, storage);
auto make_snapshot = [](const mfem::Vector &u, uint8_t *outb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double) * size_t(u.Size()), "Vector snapshot size mismatch");
std::memcpy(outb, u.GetData(), bytes);
};
auto restore_snapshot = [](mfem::Vector &u, const uint8_t *inb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double) * size_t(u.Size()), "Vector snapshot size mismatch");
std::memcpy(u.GetData(), inb, bytes);
};
auto primal_step = [factor](int /*step*/, mfem::Vector &u)
{
u *= factor;
};
auto adjoint_step = [factor](int /*step*/, const mfem::Vector & /*u_step*/, mfem::Vector &lambda)
{
lambda *= factor;
};
mfem::Vector u(dim);
for (int k = 0; k < dim; ++k) { u[k] = 1.0 + 0.1*k; }
mfem::Vector target(dim);
target = 2.0;
// Forward
for (int i = 0; i < Nsteps; ++i)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
}
mfem::Vector uN(u); // copy for reporting
// Init adjoint: lambda_N = uN - target
mfem::Vector lambda(dim);
lambda = u;
lambda -= target;
// Reverse
mfem::Vector u_work(dim);
u_work = 0.0;
for (int i = Nsteps - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
}
out << "||uN||_2 = " << uN.Norml2() << "\n";
out << "||lambda0||_2 = " << lambda.Norml2() << "\n";
}
int main(int argc, char *argv[])
{
mfem::Device device("cpu");
device.Print();
int Nsteps = 20;
int Ncheck = 3;
int dim = 5;
double factor = 1.05;
mfem::OptionsParser args(argc, argv);
args.AddOption(&Nsteps, "-n", "--num-steps", "Number of primal steps.");
args.AddOption(&Ncheck, "-s", "--num-checkpoints", "Number of checkpoints (snaps).");
args.AddOption(&dim, "-d", "--dim", "Vector dimension for the mfem::Vector example.");
args.AddOption(&factor, "-f", "--factor", "Scalar factor in the toy update u_{n+1}=f*u_n.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(out);
return 1;
}
args.PrintOptions(out);
RunScalarExample(Nsteps, Ncheck, factor);
RunVectorExample(Nsteps, Ncheck, dim, factor);
return 0;
}

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