Compare commits
146
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bef1cc28f | ||
|
|
6b45d9a69b | ||
|
|
db32ae9807 | ||
|
|
5ce6e90ceb | ||
|
|
03ba184adb | ||
|
|
cfa87477da | ||
|
|
939932bc68 | ||
|
|
d3ae34710c | ||
|
|
9b652996b2 | ||
|
|
5cfbbe5fad | ||
|
|
71909cd5e3 | ||
|
|
a5a3169064 | ||
|
|
afded067a7 | ||
|
|
629e93afd9 | ||
|
|
3e277808a9 | ||
|
|
864fb1ce9e | ||
|
|
ac7415cc69 | ||
|
|
38030d4395 | ||
|
|
2c96dc6a1f | ||
|
|
ecbc7bf8c2 | ||
|
|
5b5a21edac | ||
|
|
1f84ba036e | ||
|
|
76d0312309 | ||
|
|
c7ed339260 | ||
|
|
f1b3a33fb2 | ||
|
|
e3dedbbd5b | ||
|
|
261f3805b8 | ||
|
|
b386b2d6b6 | ||
|
|
6e98055eb7 | ||
|
|
5abd44f212 | ||
|
|
75012728db | ||
|
|
1f094244f8 | ||
|
|
cc16ddadbf | ||
|
|
8acd5cd3a2 | ||
|
|
1f5bc1c3d8 | ||
|
|
3588d47ec1 | ||
|
|
e7f5996bdf | ||
|
|
560ad1b5a3 | ||
|
|
f0fe5b0ec0 | ||
|
|
a92983051a | ||
|
|
37843b050c | ||
|
|
436714f5ef | ||
|
|
1ceef4f786 | ||
|
|
6bb6745c0e | ||
|
|
60f47c287d | ||
|
|
545a257ab5 | ||
|
|
164ee942c8 | ||
|
|
16c4fbdd29 | ||
|
|
e28093274b | ||
|
|
87dd19e6c0 | ||
|
|
678f53c306 | ||
|
|
4b4aff736d | ||
|
|
727339e0ae | ||
|
|
d8d4d5b874 | ||
|
|
5a7a017a4e | ||
|
|
224aff5ac9 | ||
|
|
e42762365f | ||
|
|
765fb4a608 | ||
|
|
32fb4bf244 | ||
|
|
2d021685de | ||
|
|
591156be2f | ||
|
|
d6ea262498 | ||
|
|
6b147fd9ff | ||
|
|
a0981cb363 | ||
|
|
894de992da | ||
|
|
8e2478f964 | ||
|
|
942249395b | ||
|
|
a3f6d5b971 | ||
|
|
06c485bb3e | ||
|
|
5546250963 | ||
|
|
5b065ad7f2 | ||
|
|
1cd27605f0 | ||
|
|
547fd02911 | ||
|
|
aa92498033 | ||
|
|
7f4d7b8f4e | ||
|
|
024576620a | ||
|
|
5b670bda3f | ||
|
|
6b0e135bdb | ||
|
|
204b9df2d6 | ||
|
|
8b49379f87 | ||
|
|
d32a00fb2f | ||
|
|
59c9ca70e4 | ||
|
|
6529372830 | ||
|
|
d66d799387 | ||
|
|
51f205b273 | ||
|
|
0161ad9d92 | ||
|
|
3589479481 | ||
|
|
e9acfeccda | ||
|
|
ad7cf12cd5 | ||
|
|
9646ce4d1a | ||
|
|
f46a81bf3a | ||
|
|
460e5ba483 | ||
|
|
abdb023ae3 | ||
|
|
34593dccac | ||
|
|
248ab78e77 | ||
|
|
6b9bdb11df | ||
|
|
505d2e5123 | ||
|
|
5cd3ec521b | ||
|
|
47cff96e2e | ||
|
|
d1a9c6e62d | ||
|
|
8f0b57138b | ||
|
|
3167a1c98b | ||
|
|
9488637956 | ||
|
|
f429737c12 | ||
|
|
04fd683e9c | ||
|
|
4b9f46a6b0 | ||
|
|
793a5b6d60 | ||
|
|
4e6e9a13b6 | ||
|
|
010f455382 | ||
|
|
acebc322b8 | ||
|
|
6f280d81b5 | ||
|
|
25ec78df9a | ||
|
|
90353c437e | ||
|
|
8c78387136 | ||
|
|
fbb50af208 | ||
|
|
8684d9d5d1 | ||
|
|
2375953e95 | ||
|
|
ecbb196551 | ||
|
|
90ca758a98 | ||
|
|
93d674a7a2 | ||
|
|
03ec3f739b | ||
|
|
a3eb9c8e6b | ||
|
|
30f3e123cd | ||
|
|
4d4c2325f9 | ||
|
|
db84abedfc | ||
|
|
2609e3af6e | ||
|
|
5b917af59b | ||
|
|
f956c6b2de | ||
|
|
62dbc570b2 | ||
|
|
c221f5a29d | ||
|
|
ce8cd01cfd | ||
|
|
a60baf8ce6 | ||
|
|
96bba18449 | ||
|
|
f9cce3ab62 | ||
|
|
292700bb52 | ||
|
|
fd2f0df34f | ||
|
|
4ac41a6427 | ||
|
|
58826d64c9 | ||
|
|
a126203ccd | ||
|
|
6656a7ef72 | ||
|
|
8804df317d | ||
|
|
95408b0fae | ||
|
|
79819a5563 | ||
|
|
d57fc7c0d9 | ||
|
|
9fb590d79d | ||
|
|
cb4ca9228f |
@@ -17,6 +17,9 @@ 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
|
||||
|
||||
+67
-28
@@ -39,8 +39,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
b_type = b_type_i;
|
||||
cp_type = cp_type_i;
|
||||
tol = tol_i;
|
||||
lbound.SetSize(nb, ncp);
|
||||
ubound.SetSize(nb, ncp);
|
||||
lbound.SetSize(ncp, nb);
|
||||
ubound.SetSize(ncp, nb);
|
||||
nodes.SetSize(nb);
|
||||
weights.SetSize(nb);
|
||||
control_points.SetSize(ncp);
|
||||
@@ -125,21 +125,25 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
}
|
||||
else if (j == ncp-1)
|
||||
{
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
vals(0) = bv(i);
|
||||
vals(1) = bmv(i) + dm*bdmv(i);
|
||||
vals(2) = bpv(i) + dp*bdpv(i);
|
||||
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
|
||||
lbound(j,i) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(j,i) = vals.Max()+tol; // tolerance for good measure
|
||||
if (b_type == 2)
|
||||
{
|
||||
lbound(j,i) = std::max(lbound(j,i),0_r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,8 +277,7 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
intmax.SetSize(ncp);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector coeffm(nb);
|
||||
coeffm = 0.0;
|
||||
Vector coeffm;
|
||||
|
||||
real_t a0 = 0.0;
|
||||
real_t a1 = 0.0;
|
||||
@@ -302,6 +305,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
// compute L2 projection for linear bases: a0 + a1*x
|
||||
if (proj)
|
||||
{
|
||||
coeffm.SetSize(nb);
|
||||
coeffm = 0.0;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1;
|
||||
@@ -342,8 +347,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t c = coeffm(i);
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmin(j) += min(lbound(j,i)*c, ubound(j,i)*c);
|
||||
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,10 +479,10 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth row
|
||||
{
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
intmin(k*ncp+i) += vals.Min();
|
||||
intmax(k*ncp+i) += vals.Max();
|
||||
}
|
||||
@@ -553,17 +558,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1; // x-coordinate
|
||||
minBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
maxBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
minNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
}
|
||||
// Compute Bernstein coefficients
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, minBounds.GetData());
|
||||
lu.Solve(nb, 1, maxBounds.GetData());
|
||||
lu.Solve(nb, 1, minNodalVals.GetData());
|
||||
lu.Solve(nb, 1, maxNodalVals.GetData());
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
intminT(i*ncp2+j) = minBounds(i);
|
||||
intmaxT(i*ncp2+j) = maxBounds(i);
|
||||
intminT(i*ncp2+j) = minNodalVals(i);
|
||||
intmaxT(i*ncp2+j) = maxNodalVals(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -617,10 +622,10 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth slice
|
||||
{
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
intmin(k*ncp2+i) += vals.Min();
|
||||
intmax(k*ncp2+i) += vals.Max();
|
||||
}
|
||||
@@ -653,7 +658,8 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
Vector &nodesBern) const
|
||||
{
|
||||
const int nbern = nodesBern.Size();
|
||||
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
|
||||
L2_SegmentElement el(nbern-1, 2);
|
||||
// we use L2 to leverage lexicographic order
|
||||
Array<int> ordering = el.GetLexicographicOrdering();
|
||||
basisMat.SetSize(nbern, nbern);
|
||||
Vector shape(nbern);
|
||||
@@ -666,6 +672,39 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetBoundingMatrix(int dim, bool is_lower) const
|
||||
{
|
||||
if (dim > 1)
|
||||
{
|
||||
const int ncpd = static_cast<int>(std::pow(ncp, dim));
|
||||
const int nbd = static_cast<int>(std::pow(nb, dim));
|
||||
DenseMatrix boundND(ncpd, nbd);
|
||||
Vector phimin, phimax, col;
|
||||
Vector coeffs(nbd);
|
||||
coeffs = 0.0;
|
||||
for (int j = 0; j < nbd; j++)
|
||||
{
|
||||
coeffs(j) = 1.0;
|
||||
boundND.GetColumnReference(j, col);
|
||||
GetNDBounds(dim, coeffs, phimin, phimax);
|
||||
col = is_lower ? phimin : phimax;
|
||||
coeffs(j) = 0.0;
|
||||
}
|
||||
return boundND;
|
||||
}
|
||||
return is_lower ? lbound : ubound;
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetLowerBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, true);
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetUpperBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, false);
|
||||
}
|
||||
|
||||
constexpr int PLBound::min_ncp_gl_x[2][11];
|
||||
constexpr int PLBound::min_ncp_gll_x[2][11];
|
||||
constexpr int PLBound::min_ncp_pos_x[2][11];
|
||||
@@ -716,4 +755,4 @@ void PLBound::Print(std::ostream &outp) const
|
||||
ubound.Print(outp);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+71
-20
@@ -19,14 +19,18 @@ namespace mfem
|
||||
{
|
||||
|
||||
/** @name Piecewise linear bounds of bases
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on
|
||||
the grid function in each element. The bounds for the bases are constructed
|
||||
based on the following parameters:
|
||||
|
||||
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
|
||||
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre
|
||||
nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
2 - Positive/Bernstein bases on uniformly distributed nodes,
|
||||
|
||||
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
|
||||
(iii) @b ncp: number of control points used to construct the piecewise
|
||||
linear bounds
|
||||
|
||||
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
|
||||
1 - Chebyshev.
|
||||
@@ -35,7 +39,9 @@ namespace mfem
|
||||
|
||||
If the user does not specify @b ncp and @b cp_type, the minimum value of
|
||||
@b ncp is used that would bound the bases for the @b cp_type. We default
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases.
|
||||
Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and
|
||||
increasing @b ncp results in tighter bounds.
|
||||
|
||||
Finally, only tensor-product elements are currently supported.
|
||||
|
||||
@@ -54,7 +60,7 @@ private:
|
||||
bool proj = true; // Use linear projection to compute bounds.
|
||||
real_t tol = 0.0; // offset bounds to avoid round-off errors
|
||||
Vector nodes, weights, control_points;
|
||||
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
|
||||
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
|
||||
// Some auxillary storage for computing the bounds with Bernstein
|
||||
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
|
||||
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
|
||||
@@ -80,6 +86,9 @@ private:
|
||||
{3,5,8,9,11,12,13,13,14,15,16}
|
||||
};
|
||||
|
||||
/// Helper function to extract lower or upper bounding matrix
|
||||
DenseMatrix GetBoundingMatrix(int dim, bool is_lower) const;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
@@ -92,40 +101,82 @@ public:
|
||||
PLBound(const FiniteElementSpace *fes,
|
||||
const int ncp_i = -1, const int cp_type_i = 0);
|
||||
|
||||
// Get minimum number of control points needed to bound the given bases
|
||||
/// Get minimum number of control points needed to bound the given bases
|
||||
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
int cp_type_i) const;
|
||||
|
||||
// Print information about the bounds
|
||||
/// Print information about the bounds
|
||||
void Print(std::ostream &outp = mfem::out) const;
|
||||
|
||||
// Enable (default) or disable linear projection before bounding.
|
||||
// This projection increases the computational cost but results in tighter
|
||||
// bounds.
|
||||
/** @brief Enable (default) or disable linear projection before bounding.
|
||||
*
|
||||
* @details This projection increases the computational cost but results in
|
||||
* tighter bounds.
|
||||
*/
|
||||
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D/2D/3D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D/2D/3D.
|
||||
*
|
||||
* @param[in] rdim The spatial dimension of the element (1, 2, or 3).
|
||||
* @param[in] coeff The vector of lexicographically-ordered coefficients.
|
||||
* Should be of size nb^rdim, where nb is the number of
|
||||
* bases/nodes in 1D. These coefficients must correspond
|
||||
* to the bases type and number of bases, used in the
|
||||
* constructor of PLBound.
|
||||
*
|
||||
* @param[out] intmin The vector of minimum bound for all control points.
|
||||
* @param[out] intmax The vector of maximum bound for all control points.
|
||||
* Both intmin and intmax are of size ncp^rdim, where
|
||||
* ncp is the number of control points in 1D, and are
|
||||
* ordered lexicographically.
|
||||
*/
|
||||
void GetNDBounds(const int rdim, const Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Get number of control points used to compute the bounds.
|
||||
int GetNControlPoints() const { return ncp; }
|
||||
|
||||
/// Get 1D control point locations (lexicographic order) in [0,1].
|
||||
const Vector &GetControlPoints() const { return control_points; }
|
||||
|
||||
/** @brief Get lower and upper bounding matrix (ncp^dim x nb^dim)
|
||||
*
|
||||
* @details The matrices can be used to compute the bounds at control points
|
||||
* by a simple matrix-vector product with the
|
||||
* lexicographically-ordered nodal coefficients.
|
||||
* The resulting output is also lexicographically-ordered.
|
||||
*
|
||||
* @note These matrices do not account for the linear projection step that
|
||||
* is optionally done in GetNDBounds before bounding the function.
|
||||
*/
|
||||
///@{
|
||||
DenseMatrix GetLowerBoundMatrix(int dim = 1) const;
|
||||
DenseMatrix GetUpperBoundMatrix(int dim = 1) const;
|
||||
///@}
|
||||
|
||||
private:
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 2D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 2D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 3D.
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 3D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
/// for Bernstein bases.
|
||||
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
* for Bernstein bases.
|
||||
*/
|
||||
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
|
||||
|
||||
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
|
||||
+215
-41
@@ -84,29 +84,33 @@ public:
|
||||
const std::vector<derivative_action_t> &derivative_actions,
|
||||
const FieldDescriptor &direction,
|
||||
const int &daction_l_size,
|
||||
const std::vector<derivative_action_t> &derivative_actions_transpose,
|
||||
const int &derivative_action_tr_l_size,
|
||||
const std::vector<derivative_action_t> &derivative_tr_actions,
|
||||
const FieldDescriptor &transpose_direction,
|
||||
const int &daction_transpose_l_size,
|
||||
const std::vector<Vector *> &solutions_l,
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::function<void(Vector &, Vector &)> &tr_prolongation_transpose,
|
||||
const std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
&assemble_derivative_sparsematrix_callbacks,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
const assemble_derivative_hypreparmatrix_callback_t
|
||||
&assemble_derivative_hypreparmatrix_callback) :
|
||||
Operator(height, width),
|
||||
derivative_actions(derivative_actions),
|
||||
direction(direction),
|
||||
daction_l(daction_l_size),
|
||||
daction_l_size(daction_l_size),
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
derivative_action_tr_l_size(derivative_action_tr_l_size),
|
||||
derivative_tr_actions(derivative_tr_actions),
|
||||
transpose_direction(transpose_direction),
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
tr_prolongation_transpose(tr_prolongation_transpose),
|
||||
assemble_derivative_sparsematrix_callbacks(
|
||||
assemble_derivative_sparsematrix_callbacks),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
assemble_derivative_hypreparmatrix_callback(
|
||||
assemble_derivative_hypreparmatrix_callback)
|
||||
{
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
@@ -156,18 +160,18 @@ public:
|
||||
/// direction_t on T-dofs.
|
||||
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
|
||||
{
|
||||
MFEM_ASSERT(!derivative_actions_transpose.empty(),
|
||||
MFEM_ASSERT(!derivative_tr_actions.empty(),
|
||||
"derivative can't be used to be multiplied in transpose mode");
|
||||
|
||||
daction_l.SetSize(width);
|
||||
daction_l.SetSize(derivative_action_tr_l_size);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(transpose_direction, direction_t, direction_l);
|
||||
for (const auto &f : derivative_actions_transpose)
|
||||
for (const auto &f : derivative_tr_actions)
|
||||
{
|
||||
f(fields_e, direction_l, daction_l);
|
||||
}
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
tr_prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Assemble the derivative operator into a SparseMatrix.
|
||||
@@ -183,6 +187,10 @@ public:
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
|
||||
// SparseMatrix A is finalized after all callbacks have contributed to
|
||||
// it.
|
||||
A->Finalize();
|
||||
}
|
||||
|
||||
/// @brief Assemble the derivative operator into a HypreParMatrix.
|
||||
@@ -191,13 +199,7 @@ public:
|
||||
/// be an uninitialized object.
|
||||
void Assemble(HypreParMatrix *&A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a HypreParMatrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
assemble_derivative_hypreparmatrix_callback(fields_e, A);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -212,10 +214,12 @@ private:
|
||||
|
||||
const int daction_l_size;
|
||||
|
||||
const int derivative_action_tr_l_size;
|
||||
|
||||
/// Transpose Derivative action callbacks. Depending on the requested
|
||||
/// derivatives in DifferentiableOperator the callbacks represent certain
|
||||
/// combinations of actions of derivatives of the forward operator.
|
||||
std::vector<derivative_action_t> derivative_actions_transpose;
|
||||
std::vector<derivative_action_t> derivative_tr_actions;
|
||||
|
||||
FieldDescriptor transpose_direction;
|
||||
|
||||
@@ -225,13 +229,15 @@ private:
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
std::function<void(Vector &, Vector &)> tr_prolongation_transpose;
|
||||
|
||||
/// Callbacks that assemble derivatives into a SparseMatrix.
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
|
||||
/// Callbacks that assemble derivatives into a HypreParMatrix.
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
assemble_derivative_hypreparmatrix_callback_t
|
||||
assemble_derivative_hypreparmatrix_callback;
|
||||
};
|
||||
|
||||
/// Class representing a differentiable operator which acts on solution and
|
||||
@@ -457,7 +463,10 @@ public:
|
||||
dir_l = s_l[derivative_idx];
|
||||
}
|
||||
|
||||
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
|
||||
for (size_t i = 0; i < derivative_setup_callbacks[derivative_id].size(); i++)
|
||||
{
|
||||
derivative_setup_callbacks[derivative_id][i](fields_e, dir_l);
|
||||
}
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
height,
|
||||
@@ -465,15 +474,17 @@ public:
|
||||
derivative_action_callbacks[derivative_id],
|
||||
fields[derivative_idx],
|
||||
residual_l.Size(),
|
||||
daction_transpose_callbacks[derivative_id],
|
||||
derivative_action_tr_l_size[derivative_id],
|
||||
derivative_action_tr_callbacks[derivative_id],
|
||||
fields[test_space_field_idx],
|
||||
GetVSize(fields[test_space_field_idx]),
|
||||
sol_l,
|
||||
par_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose,
|
||||
derivative_tr_prolongation_transpose[derivative_id],
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id],
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
assemble_derivative_hypreparmatrix_callback[derivative_id]);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -486,13 +497,15 @@ private:
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::vector<derivative_action_t>> derivative_action_tr_callbacks;
|
||||
std::map<size_t,
|
||||
std::function<void(Vector &, Vector &)>> derivative_tr_prolongation_transpose;
|
||||
std::map<size_t, int> derivative_action_tr_l_size;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
std::map<size_t, assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callback;
|
||||
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
@@ -510,7 +523,7 @@ private:
|
||||
std::function<void(Vector &, Vector &)> output_restriction_transpose;
|
||||
restriction_callback_t restriction_callback;
|
||||
|
||||
std::map<size_t, Vector> derivative_qp_caches;
|
||||
std::map<size_t, std::vector<Vector>> derivative_qp_caches;
|
||||
|
||||
std::map<size_t, size_t> assembled_vector_sizes;
|
||||
|
||||
@@ -769,9 +782,10 @@ void DifferentiableOperator::AddIntegrator(
|
||||
auto input_size_on_qp =
|
||||
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
|
||||
|
||||
// printf("calculate shmem action info\n");
|
||||
auto action_shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
|
||||
|
||||
Vector shmem_cache(action_shmem_info.total_size);
|
||||
@@ -894,9 +908,10 @@ void DifferentiableOperator::AddIntegrator(
|
||||
const int da_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
|
||||
|
||||
// printf("calculate shmem derivative action info\n");
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>(
|
||||
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
|
||||
input_dtq_maps, output_dtq_maps, fields, num_entities,
|
||||
num_qp, input_size_on_qp, residual_size_on_qp,
|
||||
element_dof_ordering, d_field_idx);
|
||||
|
||||
@@ -968,9 +983,12 @@ void DifferentiableOperator::AddIntegrator(
|
||||
// Quadrature point local derivative cache for each element, with data
|
||||
// layout:
|
||||
// [test_vdim, test_op_dim, trial_vdim, trial_op_dim, qp, num_entities].
|
||||
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
|
||||
trial_vdim *
|
||||
total_trial_op_dim * num_qp * num_entities);
|
||||
derivative_qp_caches[derivative_id].push_back(
|
||||
Vector(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim * num_qp *
|
||||
num_entities));
|
||||
|
||||
const int cache_index = this->derivative_qp_caches[derivative_id].size() - 1;
|
||||
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& fields_ref = this->fields;
|
||||
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
|
||||
@@ -1012,6 +1030,8 @@ void DifferentiableOperator::AddIntegrator(
|
||||
trial_vdim,
|
||||
inputs_trial_op_dim,
|
||||
|
||||
qpdc_idx = cache_index,
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref
|
||||
](std::vector<Vector> &f_e, const Vector &dir_l) mutable
|
||||
@@ -1024,7 +1044,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].ReadWrite(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
@@ -1095,6 +1115,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
inputs_trial_op_dim,
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
qpdc_idx = cache_index,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&or_transpose
|
||||
@@ -1112,7 +1133,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
@@ -1157,6 +1178,154 @@ void DifferentiableOperator::AddIntegrator(
|
||||
or_transpose(derivative_action_e, der_action_l);
|
||||
});
|
||||
|
||||
// This prevents Sum/Identity. These are invalid
|
||||
// as input FieldOperators anyways.
|
||||
constexpr auto dummy_fop = Value<0> {};
|
||||
|
||||
auto [input_rt,
|
||||
input_e_sz] = get_restriction_transpose<entity_t>
|
||||
(fields[d_field_idx],
|
||||
element_dof_ordering, dummy_fop);
|
||||
|
||||
const auto input_restriction_transpose = input_rt;
|
||||
|
||||
derivative_tr_prolongation_transpose[derivative_id] =
|
||||
get_prolongation_transpose(
|
||||
fields[d_field_idx], dummy_fop, mesh.GetComm());
|
||||
|
||||
const auto d_tr_field_idx = test_space_field_idx;
|
||||
const auto direction_tr = fields[d_tr_field_idx];
|
||||
|
||||
auto output_size_on_qp =
|
||||
get_input_size_on_qp(outputs, std::make_index_sequence<num_outputs> {});
|
||||
|
||||
const int residual_tr_size_on_qp = trial_vdim * total_trial_op_dim;
|
||||
|
||||
auto shmem_tr_info =
|
||||
get_shmem_info<entity_t, num_fields, num_outputs, num_inputs>(
|
||||
output_dtq_maps, input_dtq_maps, fields, num_entities,
|
||||
num_qp, output_size_on_qp, residual_tr_size_on_qp,
|
||||
element_dof_ordering, test_space_field_idx);
|
||||
|
||||
// print_shared_memory_info(shmem_tr_info);
|
||||
|
||||
// TODO: this is a hack to extend the shared memory with a known
|
||||
// offset for a temp variable
|
||||
Vector shmem_tr_cache(shmem_tr_info.total_size + residual_tr_size_on_qp *
|
||||
num_qp);
|
||||
|
||||
Vector direction_tr_e(get_restriction<entity_t>(
|
||||
fields[test_space_field_idx],
|
||||
element_dof_ordering)->Height());
|
||||
|
||||
derivative_action_tr_l_size[derivative_id] =
|
||||
get_restriction<entity_t>(fields[d_field_idx],
|
||||
element_dof_ordering)->Width();
|
||||
|
||||
Vector derivative_action_tr_e(input_e_sz);
|
||||
derivative_action_tr_e = 0.0;
|
||||
|
||||
derivative_action_tr_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_trial_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
outputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
// output_fop, // class derived from FieldOperator
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_tr_cache, // Vector (local)
|
||||
shmem_tr_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent,
|
||||
direction_tr, // FieldDescriptor
|
||||
direction_tr_e, // Vector
|
||||
derivative_action_tr_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
inputs_trial_op_dim,
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
input_restriction_transpose,
|
||||
qpdc_idx = cache_index,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_tr_l,
|
||||
Vector &derivative_action_tr_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction_tr, dir_tr_l, direction_tr_e,
|
||||
element_dof_ordering);
|
||||
auto ye = Reshape(derivative_action_tr_e.ReadWrite(), num_trial_dof,
|
||||
trial_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_tr_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_tr_e.ReadWrite(),
|
||||
shmem_tr_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_attr = attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
|
||||
derivative_action_tr_e = 0.0;
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [output_dtq_shmem, input_dtq_shmem, fields_shmem,
|
||||
direction_shmem, input_shmem,
|
||||
shadow_shmem_, residual_shmem,
|
||||
scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_tr_info, output_dtq_maps, input_dtq_maps,
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
std::array<bool, num_outputs> all_true{true};
|
||||
map_direction_to_quadrature_data_conditional(
|
||||
shadow_shmem, direction_shmem, output_dtq_shmem, outputs,
|
||||
ir_weights, scratch_shmem, all_true, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), trial_vdim,
|
||||
total_trial_op_dim, num_qp);
|
||||
|
||||
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
|
||||
constexpr bool transpose = true;
|
||||
apply_qpdc(fhat, shadow_shmem, qpdce, itod, q1d, dimension,
|
||||
use_sum_factorization, transpose);
|
||||
|
||||
auto y = Reshape(&ye(0, 0, e), num_trial_dof, trial_vdim);
|
||||
auto fi_shmem = Reshape(shmem + shmem_tr_info.total_size, trial_vdim,
|
||||
total_trial_op_dim, num_qp);
|
||||
|
||||
map_quadrature_data_to_fields_conditional(
|
||||
y, fhat, inputs, itod, input_dtq_shmem, scratch_shmem, fi_shmem,
|
||||
input_is_dependent, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_tr_info.total_size,
|
||||
shmem_tr_cache.ReadWrite());
|
||||
input_restriction_transpose(derivative_action_tr_e, derivative_action_tr_l);
|
||||
});
|
||||
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
@@ -1190,7 +1359,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
inputs_trial_op_dim,
|
||||
Ae_mem,
|
||||
output_to_field,
|
||||
|
||||
qpdc_idx = cache_index,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&fields = fields_ref
|
||||
@@ -1202,7 +1371,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
@@ -1250,7 +1419,10 @@ void DifferentiableOperator::AddIntegrator(
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
if (A == nullptr)
|
||||
{
|
||||
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
}
|
||||
|
||||
auto tmp = Reshape(Ae_mem.HostReadWrite(), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim, num_entities);
|
||||
@@ -1321,14 +1493,15 @@ void DifferentiableOperator::AddIntegrator(
|
||||
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
|
||||
}
|
||||
}
|
||||
A->Finalize();
|
||||
// Don't finalize here since multiple callbacks might contribute to the same matrix
|
||||
// A->Finalize() will be called after all callbacks have contributed
|
||||
});
|
||||
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& assemble_derivative_sparsematrix_callbacks_ref =
|
||||
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
|
||||
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
|
||||
assemble_derivative_hypreparmatrix_callback[derivative_id] =
|
||||
[
|
||||
input_is_dependent,
|
||||
input_to_field,
|
||||
@@ -1342,6 +1515,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
{
|
||||
f(f_e, spmat);
|
||||
}
|
||||
spmat->Finalize();
|
||||
|
||||
if (spmat == nullptr)
|
||||
{
|
||||
@@ -1395,7 +1569,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
trial_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
delete spmat;
|
||||
});
|
||||
};
|
||||
}, derivative_ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,4 +530,65 @@ void map_quadrature_data_to_fields(
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_conditional(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &op_dims,
|
||||
const std::array<DofToQuadMap, N> &dtqmaps,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const DeviceTensor<3> &fi_shmem,
|
||||
const std::array<bool, N> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
int offset = 0;
|
||||
for_constexpr<N>([&](auto i)
|
||||
{
|
||||
if (conditions[i])
|
||||
{
|
||||
[[maybe_unused]] const auto [K, unused, M] = f.GetShape();
|
||||
const int L = static_cast<int>(op_dims(static_cast<size_t>(i)));
|
||||
auto fi = Reshape(&fi_shmem(0, 0, 0), K, L, M);
|
||||
for (int k = 0; k < K; k++)
|
||||
{
|
||||
for (int l = 0; l < L; l++)
|
||||
{
|
||||
for (int m = 0; m < M; m++)
|
||||
{
|
||||
fi(k, l, m) = f(k, l + offset, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_1d(
|
||||
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("dimension not supported"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
map_quadrature_data_to_fields_impl(y, fi, get<i>(fops), dtqmaps[i]);
|
||||
}
|
||||
offset += L;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
|
||||
+12
-17
@@ -505,13 +505,13 @@ void map_field_to_quadrature_data(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
|
||||
template <typename field_operator_ts, size_t N, size_t M>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_fields_to_quadrature_data(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<size_t, num_inputs> &input_to_field,
|
||||
std::array<DeviceTensor<2>, N> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, M> &fields_e,
|
||||
const std::array<DofToQuadMap, N> &dtqmaps,
|
||||
const std::array<size_t, N> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
@@ -523,7 +523,7 @@ void map_fields_to_quadrature_data(
|
||||
// attached to them and we create a dummy field which is not accessed
|
||||
// inside the functions it is passed to.
|
||||
const auto dummy_field_weight = DeviceTensor<1>(nullptr, 0);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
for_constexpr<N>([&](auto i)
|
||||
{
|
||||
const DeviceTensor<1> &field_e =
|
||||
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
|
||||
@@ -549,12 +549,7 @@ void map_fields_to_quadrature_data(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("unsupported dimension");
|
||||
#endif
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("unsupported dimension"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -627,20 +622,20 @@ void map_fields_to_quadrature_data_conditional(
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t num_inputs, typename field_operator_ts>
|
||||
template <size_t N, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_direction_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
|
||||
std::array<DeviceTensor<2>, N> &directions_qp,
|
||||
const DeviceTensor<1> &direction_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<DofToQuadMap, N> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const std::array<bool, N> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
for_constexpr<N>([&](auto i)
|
||||
{
|
||||
if (conditions[i])
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@ protected:
|
||||
};
|
||||
|
||||
/// @brief Uniform parameter space
|
||||
class UniformParameterSpace : public ParameterSpace
|
||||
class UniformParameterSpace final : public ParameterSpace
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for a uniform parameter space
|
||||
|
||||
@@ -379,54 +379,102 @@ namespace detail
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param op_dims operator dimensions.
|
||||
/// If an operator is dependent, the value corresponds to the spatial dimension.
|
||||
/// Otherwise a zero indicates indepence on the variable.
|
||||
/// @param q the current quadrature point index.
|
||||
template <size_t num_fields>
|
||||
/// @param transpose switch to use transpose action.
|
||||
template <size_t N>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const std::array<DeviceTensor<2>, N> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q)
|
||||
const DeviceTensor<1, const real_t> &op_dims,
|
||||
const int &q,
|
||||
bool transpose)
|
||||
{
|
||||
const size_t num_ops = op_dims.GetShape()[0];
|
||||
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
const size_t num_inputs = itod.GetShape()[0];
|
||||
const int total_trial_op_dim = qpdc.GetShape()[3];
|
||||
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
|
||||
if (transpose)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
for (int m = 0; m < total_trial_op_dim; m++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(itod(s));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto d_qp =
|
||||
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
fhat(j, m, q) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we don't support more than output space right now
|
||||
// shadow_shmem will always be of size 1.
|
||||
constexpr int shadow_idx_tr = 0;
|
||||
auto d_qp = Reshape(&(shadow_shmem[shadow_idx_tr])[0], test_vdim, test_op_dim,
|
||||
num_qp);
|
||||
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_ops; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(op_dims(s));
|
||||
if (trial_op_dim == 0) { continue; }
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t contrib = qpdc(i, k, j, m + m_offset, q) * d_qp(i, k, q);
|
||||
sum += contrib;
|
||||
}
|
||||
}
|
||||
fhat(j, m + m_offset, q) += sum;
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_ops; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(op_dims(s));
|
||||
if (trial_op_dim == 0) { continue; }
|
||||
|
||||
const auto d_qp =
|
||||
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
fhat(i, k, q) = sum;
|
||||
}
|
||||
fhat(i, k, q) = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @brief Apply the quadrature point data cache (qpdc) to a vector
|
||||
/// (usually a direction).
|
||||
@@ -445,16 +493,18 @@ void apply_qpdc(
|
||||
/// @param q1d number of quadrature points in 1D.
|
||||
/// @param dimension spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
template <size_t num_fields>
|
||||
/// @param T switch to use transpose application.
|
||||
template <size_t N>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const std::array<DeviceTensor<2>, N> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
const bool &use_sum_factorization,
|
||||
const bool T = false)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
@@ -462,7 +512,7 @@ void apply_qpdc(
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
@@ -472,7 +522,7 @@ void apply_qpdc(
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -485,7 +535,7 @@ void apply_qpdc(
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -500,7 +550,7 @@ void apply_qpdc(
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +243,35 @@ void process_qf_arg(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i) = u(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * n) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
|
||||
@@ -327,20 +356,4 @@ void process_qf_result(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * n) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
|
||||
+9
-3
@@ -1202,7 +1202,14 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
|
||||
const Operator *P = get_prolongation(f);
|
||||
auto PT = [=](const Vector &r_local, Vector &y)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
if (P)
|
||||
{
|
||||
P->MultTranspose(r_local, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
y = r_local;
|
||||
}
|
||||
};
|
||||
return PT;
|
||||
}
|
||||
@@ -1580,14 +1587,13 @@ struct SharedMemoryInfo
|
||||
std::array<int, 6> temp_sizes;
|
||||
};
|
||||
|
||||
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs, typename input_t>
|
||||
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs>
|
||||
SharedMemoryInfo<num_fields, num_inputs, num_outputs>
|
||||
get_shmem_info(
|
||||
const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
|
||||
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
|
||||
const std::vector<FieldDescriptor> &fields,
|
||||
const int &num_entities,
|
||||
const input_t &inputs,
|
||||
const int &num_qp,
|
||||
const std::vector<int> &input_size_on_qp,
|
||||
const int &residual_size_on_qp,
|
||||
|
||||
+40
-39
@@ -663,58 +663,59 @@ const
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
// If the new Dof2Quad is already present, e.g. added in a previous call
|
||||
// or added by another omp thread, return.
|
||||
// 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))
|
||||
{ return; }
|
||||
|
||||
// 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++)
|
||||
DofToQuad::LEXICOGRAPHIC_FULL) == nullptr)
|
||||
{
|
||||
for (int d = 0; d < b_dim; d++)
|
||||
// Undo the native ordering which is what FiniteElement::GetDofToQuad
|
||||
// returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
|
||||
const int b_dim = (range_type == VECTOR) ? dim : 1;
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
for (int d = 0; d < b_dim; d++)
|
||||
{
|
||||
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
|
||||
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
|
||||
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
|
||||
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
|
||||
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int g_dim = [this]()
|
||||
{
|
||||
switch (deriv_type)
|
||||
const int g_dim = [this]()
|
||||
{
|
||||
case GRAD: return dim;
|
||||
case DIV: return 1;
|
||||
case CURL: return cdim;
|
||||
default: return 0;
|
||||
}
|
||||
}();
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int d = 0; d < g_dim; d++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
switch (deriv_type)
|
||||
{
|
||||
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
|
||||
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
|
||||
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
|
||||
case GRAD: return dim;
|
||||
case DIV: return 1;
|
||||
case CURL: return cdim;
|
||||
default: return 0;
|
||||
}
|
||||
}();
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int d = 0; d < g_dim; d++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
|
||||
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
|
||||
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q_new);
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q_new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -589,7 +589,7 @@ void H1_TriangleElement::CalcHessian(const IntegrationPoint &ip,
|
||||
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
|
||||
Vector dshape_x(p + 1), dshape_y(p + 1), dshape_l(p + 1);
|
||||
Vector ddshape_x(p + 1), ddshape_y(p + 1), ddshape_l(p + 1);
|
||||
DenseMatrix ddu(dof, dim);
|
||||
DenseMatrix ddu(dof, (dim*(dim+1))/2);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x, ddshape_x);
|
||||
|
||||
+13
-19
@@ -282,14 +282,7 @@ int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
|
||||
void FiniteElementSpace::AdjustVDofs(Array<int> &vdofs)
|
||||
{
|
||||
int n = vdofs.Size(), *vdof = vdofs;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int j;
|
||||
if ((j = vdof[i]) < 0)
|
||||
{
|
||||
vdof[i] = -1-j;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) { vdof[i] = UnsignIndex(vdof[i]); }
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
@@ -483,13 +476,14 @@ void FiniteElementSpace::ReorderElementToDofTable()
|
||||
for (int k = 0, dof_counter = 0; k < nnz; k++)
|
||||
{
|
||||
const int sdof = J[k]; // signed dof
|
||||
const int dof = (sdof < 0) ? -1-sdof : sdof;
|
||||
const int dof = UnsignIndex(sdof);
|
||||
int new_dof = dof_marker[dof];
|
||||
if (new_dof < 0)
|
||||
{
|
||||
dof_marker[dof] = new_dof = dof_counter++;
|
||||
}
|
||||
J[k] = (sdof < 0) ? -1-new_dof : new_dof; // preserve the sign of sdof
|
||||
// Preserve the sign of sdof
|
||||
J[k] = (sdof < 0) ? FlipIndexSign(new_dof) : new_dof;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +541,7 @@ void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
|
||||
{
|
||||
for (auto d : dofs)
|
||||
{
|
||||
mark_array[d >= 0 ? d : -1 - d] = -1;
|
||||
mark_array[UnsignIndex(d)] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -931,7 +925,7 @@ void FiniteElementSpace::AddDependencies(
|
||||
if (std::abs(coef) > 1e-12)
|
||||
{
|
||||
const int mdof = master_dofs[j];
|
||||
if (mdof != sdof && mdof != (-1-sdof))
|
||||
if (mdof != sdof && mdof != FlipIndexSign(sdof))
|
||||
{
|
||||
deps.Add(sdof, mdof, coef);
|
||||
}
|
||||
@@ -1024,7 +1018,7 @@ int FiniteElementSpace::GetDegenerateFaceDofs(int index, Array<int> &dofs,
|
||||
// FiniteElementSpace::AddDependencies.
|
||||
|
||||
Array<int> edof;
|
||||
int order = GetEdgeDofs(-1 - index, edof, variant);
|
||||
int order = GetEdgeDofs(FlipIndexSign(index), edof, variant);
|
||||
|
||||
int nv = fec->DofForGeometry(Geometry::POINT);
|
||||
int ne = fec->DofForGeometry(Geometry::SEGMENT);
|
||||
@@ -1710,8 +1704,8 @@ SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
|
||||
|
||||
for (int i = 0; i < fine_ldof; i++)
|
||||
{
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (!mark[m])
|
||||
{
|
||||
@@ -1772,7 +1766,7 @@ SparseMatrix *FiniteElementSpace::VariableOrderRefinementMatrix(
|
||||
for (int i = 0; i < fine_ldof; i++)
|
||||
{
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (!mark[m])
|
||||
{
|
||||
@@ -2482,8 +2476,8 @@ SparseMatrix* FiniteElementSpace::DerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
@@ -3201,7 +3195,7 @@ void FiniteElementSpace::CalcEdgeFaceVarOrders(
|
||||
else
|
||||
{
|
||||
// degenerate face (i.e., edge-face constraint)
|
||||
slave_orders |= edge_orders[-1 - slave.index];
|
||||
slave_orders |= edge_orders[FlipIndexSign(slave.index)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1150,7 +1150,7 @@ public:
|
||||
|
||||
/// Helper to return the DOF associated with a sign encoded DOF
|
||||
static inline int DecodeDof(int dof)
|
||||
{ return (dof >= 0) ? dof : (-1 - dof); }
|
||||
{ return UnsignIndex(dof); }
|
||||
|
||||
/// Helper to determine the DOF and sign of a sign encoded DOF
|
||||
static inline int DecodeDof(int dof, real_t& sign)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -5117,6 +5118,103 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
const PLBound &plb,
|
||||
const Vector &ref_range,
|
||||
const int vdim,
|
||||
Vector &lower, Vector &upper,
|
||||
Vector &control_pos) const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
const IntegrationRule ir_in = fe->GetNodes();
|
||||
IntegrationRule ir_new(ir_in.GetNPoints());
|
||||
const int dim = fes->GetMesh()->Dimension();
|
||||
const L2_FECollection *l2fec = dynamic_cast<const L2_FECollection *>
|
||||
(fes->FEColl());
|
||||
|
||||
const TensorBasisElement *tbe =
|
||||
dynamic_cast<const TensorBasisElement *>(fe);
|
||||
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
|
||||
|
||||
const Array<int> &dof_map = tbe->GetDofMap();
|
||||
bool lexico = (dof_map.Size() == 0);
|
||||
bool bern = (tbe->GetBasisType() == BasisType::Positive);
|
||||
bool h1 = (l2fec == nullptr);
|
||||
|
||||
Vector loc_data; // gridfunction values
|
||||
// Construct an integration rule to evaluate the gridfunction in
|
||||
// subinterval.
|
||||
for (int i = 0; i < ir_in.GetNPoints(); i++)
|
||||
{
|
||||
IntegrationPoint &ip_new = ir_new.IntPoint(i);
|
||||
const IntegrationPoint &ip_old =
|
||||
ir_in.IntPoint((lexico || bern) ? i : dof_map[i]);
|
||||
Vector ip_coord(dim);
|
||||
ip_old.Get(ip_coord.GetData(), dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
ip_coord(d) = ref_range(d) +
|
||||
(ref_range(dim+d) - ref_range(d)) * ip_coord(d);
|
||||
}
|
||||
ip_new.Set(ip_coord.GetData(), dim);
|
||||
}
|
||||
GetValues(elem, ir_new, loc_data, vdim);
|
||||
// At this point, the loc_data contains function values ordered
|
||||
// lexicographically, unless we are using Bernstein bases.
|
||||
// For Bernstein, we need to project and get coefficients first.
|
||||
|
||||
// For bernstein, we get coefficients corresponding to these function values
|
||||
if (bern)
|
||||
{
|
||||
int bt = 4; // BasisType::ClosedUniform
|
||||
int o = fe->GetOrder();
|
||||
DenseMatrix projmat;
|
||||
NodalTensorFiniteElement *ntfe = nullptr;
|
||||
if (dim == 1)
|
||||
{
|
||||
if (h1) { ntfe = new H1_SegmentElement(o, bt); }
|
||||
else { ntfe = new L2_SegmentElement(o, bt); }
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
if (h1) { ntfe = new H1_QuadrilateralElement(o, bt); }
|
||||
else { ntfe = new L2_QuadrilateralElement(o, bt); }
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
if (h1) { ntfe = new H1_HexahedronElement(o, bt); }
|
||||
else { ntfe = new L2_HexahedronElement(o, bt); }
|
||||
}
|
||||
// projection matrix from H1 to Positive
|
||||
ElementTransformation *eltran = fes->GetElementTransformation(elem);
|
||||
fe->Project(*ntfe, *eltran, projmat);
|
||||
Vector loc_data_temp(loc_data.Size());
|
||||
projmat.Mult(loc_data, loc_data_temp);
|
||||
for (int i = 0; i < dof_map.Size(); i++)
|
||||
{
|
||||
loc_data(i) = loc_data_temp(dof_map[i]);
|
||||
}
|
||||
if (dof_map.Size() == 0) { loc_data = loc_data_temp; }
|
||||
delete ntfe;
|
||||
}
|
||||
|
||||
// Get bounds at control points
|
||||
plb.GetNDBounds(dim, loc_data, lower, upper);
|
||||
|
||||
// Save control point positions
|
||||
int ncp = plb.GetNControlPoints();
|
||||
control_pos.SetSize(dim * ncp);
|
||||
const Vector control_pos_1D = plb.GetControlPoints();
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
control_pos(i + d*ncp) =
|
||||
ref_range(d) + (ref_range(dim+d)-ref_range(d))*control_pos_1D(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim) const
|
||||
@@ -5197,6 +5295,467 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
return plb;
|
||||
}
|
||||
|
||||
struct IntervalNode
|
||||
{
|
||||
real_t val_min;
|
||||
real_t val_max;
|
||||
Array<IntervalNode *> child;
|
||||
IntervalNode(real_t vmin, real_t vmax)
|
||||
: val_min(vmin), val_max(vmax)
|
||||
{
|
||||
child.SetSize(0);
|
||||
}
|
||||
void AddChild(IntervalNode *ch) { child.Append(ch); }
|
||||
real_t GetChildMinLower()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_min;
|
||||
}
|
||||
real_t valmin = numeric_limits<real_t>::max();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMinLower();
|
||||
valmin = std::min(valmin, candidate);
|
||||
}
|
||||
return valmin;
|
||||
}
|
||||
real_t GetChildMinUpper()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_max;
|
||||
}
|
||||
real_t valmax = numeric_limits<real_t>::max();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMinUpper();
|
||||
valmax = std::min(valmax, candidate);
|
||||
}
|
||||
return valmax;
|
||||
}
|
||||
real_t GetChildMaxLower()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_min;
|
||||
}
|
||||
real_t valmin = numeric_limits<real_t>::lowest();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMaxLower();
|
||||
valmin = std::max(valmin, candidate);
|
||||
}
|
||||
return valmin;
|
||||
}
|
||||
real_t GetChildMaxUpper()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_max;
|
||||
}
|
||||
real_t valmax = numeric_limits<real_t>::lowest();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMaxUpper();
|
||||
valmax = std::max(valmax, candidate);
|
||||
}
|
||||
return valmax;
|
||||
}
|
||||
void DeleteChildren()
|
||||
{
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
child[i]->DeleteChildren();
|
||||
delete child[i];
|
||||
}
|
||||
child.SetSize(0);
|
||||
}
|
||||
};
|
||||
|
||||
struct SearchInterval
|
||||
{
|
||||
Vector ref_range;
|
||||
int depth;
|
||||
IntervalNode *node;
|
||||
SearchInterval(const Vector &ref_range_in, int d, IntervalNode *n)
|
||||
: ref_range(ref_range_in), depth(d), node(n)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct IntervalCompareMin
|
||||
{
|
||||
bool operator()(const SearchInterval *a, const SearchInterval *b) const
|
||||
{
|
||||
return a->node->val_min > b->node->val_min;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntervalCompareMax
|
||||
{
|
||||
bool operator()(const SearchInterval *a, const SearchInterval *b) const
|
||||
{
|
||||
return a->node->val_max < b->node->val_max;
|
||||
}
|
||||
};
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol) const
|
||||
{
|
||||
real_t min_threshold = std::numeric_limits<real_t>::max();
|
||||
return EstimateFunctionMinimum(elem, plb, vdim, max_depth, tol,
|
||||
min_threshold);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol, real_t &min_threshold) const
|
||||
{
|
||||
const int dim = this->FESpace()->GetMesh()->Dimension();
|
||||
const int ncp = plb.GetNControlPoints();
|
||||
Vector pos_range(2*dim); pos_range = 0.0;
|
||||
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
|
||||
Vector lower, upper, cp_ref_loc;
|
||||
|
||||
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
|
||||
real_t val_min = lower.Min();
|
||||
real_t val_max = upper.Min();
|
||||
|
||||
min_threshold = std::min(min_threshold, val_max);
|
||||
|
||||
// Pruning: if the element's lower bound is greater than the current global
|
||||
// upper bound, this element cannot contain the global minimum.
|
||||
if (val_min >= min_threshold)
|
||||
{
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
|
||||
if (val_min == val_max || max_depth == 0)
|
||||
{
|
||||
min_threshold = std::min(min_threshold, val_min);
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
real_t abs_tol = tol*(val_max-val_min);
|
||||
|
||||
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
|
||||
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
|
||||
initial_node);
|
||||
|
||||
std::priority_queue<SearchInterval*,
|
||||
std::vector<SearchInterval*>, IntervalCompareMin> pq;
|
||||
pq.push(initial_interval);
|
||||
|
||||
real_t min_upper_bound = upper.Min();
|
||||
real_t min_lower_bound = lower.Min();
|
||||
|
||||
while (!pq.empty())
|
||||
{
|
||||
SearchInterval *current = pq.top();
|
||||
pq.pop();
|
||||
int curr_depth = current->depth;
|
||||
|
||||
// Reached max depth or this interval cannot contain the global minimum
|
||||
if (current->node->val_min >= min_threshold || curr_depth >= max_depth)
|
||||
{
|
||||
delete current;
|
||||
continue;
|
||||
}
|
||||
|
||||
min_lower_bound = initial_node->GetChildMinLower();
|
||||
if (min_upper_bound - min_lower_bound < abs_tol)
|
||||
{
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subdivide the interval and get bounds on it
|
||||
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
|
||||
vdim, lower, upper, cp_ref_loc);
|
||||
|
||||
// process the bounds and create sub-intervals
|
||||
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
|
||||
{
|
||||
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
|
||||
{
|
||||
for (int i = 0; i < ncp-1; i++)
|
||||
{
|
||||
real_t lv = 0.0, uv = 0.0;
|
||||
if (dim == 1)
|
||||
{
|
||||
lv = std::min(lower(i), lower(i+1));
|
||||
uv = std::min(upper(i), upper(i+1));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
lv = std::min({lower(i + j*ncp), lower((i+1) + j*ncp),
|
||||
lower(i + (j+1)*ncp),
|
||||
lower((i+1) + (j+1)*ncp)});
|
||||
uv = std::min({upper(i + j*ncp), upper((i+1) + j*ncp),
|
||||
upper(i + (j+1)*ncp),
|
||||
upper((i+1) + (j+1)*ncp)});
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
lv = std::min({lower(i + j*ncp + k*ncp*ncp),
|
||||
lower((i+1) + j*ncp + k*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + k*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
lower(i + j*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
uv = std::min({upper(i + j*ncp + k*ncp*ncp),
|
||||
upper((i+1) + j*ncp + k*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + k*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
upper(i + j*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
}
|
||||
IntervalNode *child_node = new IntervalNode(lv, uv);
|
||||
current->node->AddChild(child_node);
|
||||
|
||||
if (lv < min_threshold)
|
||||
{
|
||||
min_upper_bound = std::min(min_upper_bound, uv);
|
||||
min_threshold = std::min(min_threshold, uv);
|
||||
if (curr_depth < max_depth)
|
||||
{
|
||||
pos_range(0) = cp_ref_loc(i);
|
||||
pos_range(0+dim) = cp_ref_loc(i+1);
|
||||
if (dim >= 2)
|
||||
{
|
||||
pos_range(1) = cp_ref_loc(ncp + j);
|
||||
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
pos_range(2) = cp_ref_loc(2*ncp + k);
|
||||
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
|
||||
}
|
||||
SearchInterval *child_interval =
|
||||
new SearchInterval(pos_range, curr_depth + 1,
|
||||
child_node);
|
||||
pq.push(child_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
|
||||
// clean up remaining intervals in queue
|
||||
while (!pq.empty())
|
||||
{
|
||||
delete pq.top();
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
min_lower_bound = initial_node->GetChildMinLower();
|
||||
initial_node->DeleteChildren();
|
||||
delete initial_node;
|
||||
|
||||
min_threshold = std::min(min_threshold, min_lower_bound);
|
||||
return std::make_pair(min_lower_bound, min_upper_bound);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol) const
|
||||
{
|
||||
real_t max_threshold = std::numeric_limits<real_t>::lowest();
|
||||
return EstimateFunctionMaximum(elem, plb, vdim, max_depth, tol,
|
||||
max_threshold);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol, real_t &max_threshold) const
|
||||
{
|
||||
const int dim = this->FESpace()->GetMesh()->Dimension();
|
||||
const int ncp = plb.GetNControlPoints();
|
||||
Vector pos_range(2*dim); pos_range = 0.0;
|
||||
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
|
||||
Vector lower, upper, cp_ref_loc;
|
||||
|
||||
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
|
||||
real_t val_min = lower.Max();
|
||||
real_t val_max = upper.Max();
|
||||
|
||||
max_threshold = std::max(max_threshold, val_min);
|
||||
|
||||
// Pruning: if the element's upper bound is less than the current global
|
||||
// lower bound, this element cannot contain the global maximum.
|
||||
if (val_max <= max_threshold)
|
||||
{
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
|
||||
if (val_min == val_max || max_depth == 0)
|
||||
{
|
||||
max_threshold = std::max(max_threshold, val_max);
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
real_t abs_tol = tol*(val_max-val_min);
|
||||
|
||||
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
|
||||
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
|
||||
initial_node);
|
||||
|
||||
std::priority_queue<SearchInterval*,
|
||||
std::vector<SearchInterval*>, IntervalCompareMax> pq;
|
||||
pq.push(initial_interval);
|
||||
|
||||
real_t max_lower_bound = val_min;
|
||||
real_t max_upper_bound = val_max;
|
||||
|
||||
while (!pq.empty())
|
||||
{
|
||||
SearchInterval *current = pq.top();
|
||||
pq.pop();
|
||||
int curr_depth = current->depth;
|
||||
|
||||
// Reached max depth or this interval cannot contain the global maximum.
|
||||
if (current->node->val_max <= max_threshold || curr_depth >= max_depth)
|
||||
{
|
||||
delete current;
|
||||
continue;
|
||||
}
|
||||
|
||||
max_upper_bound = initial_node->GetChildMaxUpper();
|
||||
if (max_upper_bound - max_lower_bound < abs_tol)
|
||||
{
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subdivide the interval and get bounds on it
|
||||
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
|
||||
vdim, lower, upper, cp_ref_loc);
|
||||
|
||||
// process the bounds and create sub-intervals
|
||||
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
|
||||
{
|
||||
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
|
||||
{
|
||||
for (int i = 0; i < ncp-1; i++)
|
||||
{
|
||||
real_t lv = 0.0, uv = 0.0;
|
||||
if (dim == 1)
|
||||
{
|
||||
lv = std::max(lower(i), lower(i+1));
|
||||
uv = std::max(upper(i), upper(i+1));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
lv = std::max({lower(i + j*ncp), lower((i+1) + j*ncp),
|
||||
lower(i + (j+1)*ncp),
|
||||
lower((i+1) + (j+1)*ncp)});
|
||||
uv = std::max({upper(i + j*ncp), upper((i+1) + j*ncp),
|
||||
upper(i + (j+1)*ncp),
|
||||
upper((i+1) + (j+1)*ncp)});
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
lv = std::max({lower(i + j*ncp + k*ncp*ncp),
|
||||
lower((i+1) + j*ncp + k*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + k*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
lower(i + j*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
uv = std::max({upper(i + j*ncp + k*ncp*ncp),
|
||||
upper((i+1) + j*ncp + k*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + k*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
upper(i + j*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
}
|
||||
IntervalNode *child_node = new IntervalNode(lv, uv);
|
||||
current->node->AddChild(child_node);
|
||||
|
||||
if (uv > max_threshold)
|
||||
{
|
||||
max_lower_bound = std::max(max_lower_bound, lv);
|
||||
max_threshold = std::max(max_threshold, lv);
|
||||
if (curr_depth < max_depth)
|
||||
{
|
||||
pos_range(0) = cp_ref_loc(i);
|
||||
pos_range(0+dim) = cp_ref_loc(i+1);
|
||||
if (dim >= 2)
|
||||
{
|
||||
pos_range(1) = cp_ref_loc(ncp + j);
|
||||
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
pos_range(2) = cp_ref_loc(2*ncp + k);
|
||||
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
|
||||
}
|
||||
SearchInterval *child_interval =
|
||||
new SearchInterval(pos_range, curr_depth + 1,
|
||||
child_node);
|
||||
pq.push(child_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
// clean up remaining intervals in queue
|
||||
while (!pq.empty())
|
||||
{
|
||||
delete pq.top();
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
max_upper_bound = initial_node->GetChildMaxUpper();
|
||||
initial_node->DeleteChildren();
|
||||
delete initial_node;
|
||||
max_threshold = std::max(max_threshold, max_upper_bound);
|
||||
|
||||
return std::make_pair(max_lower_bound, max_upper_bound);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
real_t global_min_lower = std::numeric_limits<real_t>::max();
|
||||
real_t global_min_upper = std::numeric_limits<real_t>::max();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
std::pair<real_t, real_t> min_pair =
|
||||
EstimateFunctionMinimum(i, plb, vdim, max_depth, tol,
|
||||
global_min_lower);
|
||||
global_min_upper = std::min(global_min_upper, min_pair.second);
|
||||
}
|
||||
return std::make_pair(global_min_lower, global_min_upper);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
real_t global_max_lower = std::numeric_limits<real_t>::lowest();
|
||||
real_t global_max_upper = std::numeric_limits<real_t>::lowest();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
std::pair<real_t, real_t> max_pair =
|
||||
EstimateFunctionMaximum(i, plb, vdim, max_depth, tol,
|
||||
global_max_upper);
|
||||
global_max_lower = std::max(global_max_lower, max_pair.first);
|
||||
}
|
||||
return std::make_pair(global_max_lower, global_max_upper);
|
||||
}
|
||||
|
||||
}
|
||||
+117
-7
@@ -564,6 +564,70 @@ protected:
|
||||
/// P-refinement version of Update().
|
||||
void UpdatePRef();
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem
|
||||
* if it is below a certain @a min_threshold.
|
||||
*
|
||||
* @details For a given element \p elem and grid function component \p vdim
|
||||
* an estimate of the function minimum is the minimum of the piecewise
|
||||
* linear lower bound obtained using the given PLBound object. The actual
|
||||
* minimum is between [minimum lower bound, minimum upper bound]. We
|
||||
* improve the estimate of the function minimum by recursively
|
||||
* subdividing the interval with the lowest lower bound, and computing
|
||||
* bounds on the sub-intervals.
|
||||
* This process continues until (i) the maximum recursion depth is reached
|
||||
* or (ii) the difference between the minimum upper bound and minimum lower
|
||||
* bound is less than a certain tolerance (\p tol * [initial maximum
|
||||
* upper bound - initial minimum lower bound]).
|
||||
* The function also terminates if the lowest minima estimate is found
|
||||
* to be above the given threshold \p min_threshold. This is useful when
|
||||
* we are interested in computing the global minimum of the function
|
||||
* over all elements. In this case we can reject elements where the lowest
|
||||
* bound is above the current global minimum. In case the function
|
||||
* minimum on the element is below the global minimum, we update
|
||||
* \p min_threshold.
|
||||
*
|
||||
* We return a pair of values that bracket the actual minimum, i.e.
|
||||
* [min_lower_bound, min_upper_bound].
|
||||
*/
|
||||
std::pair<real_t,real_t> EstimateFunctionMinimum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol,
|
||||
real_t &min_threshold)const;
|
||||
|
||||
/** @brief Estimate the maximum value of the GridFunction in element @a elem
|
||||
* if it is below a certain @a max_threshold.
|
||||
*
|
||||
* @details For a given element \p elem and grid function component \p vdim
|
||||
* an estimate of the function maximum is the maximum of the piecewise
|
||||
* linear upper bound obtained using the given PLBound object. The actual
|
||||
* maximum is between [maximum lower bound, maximum upper bound]. We
|
||||
* improve the estimate of the function maximum by recursively
|
||||
* subdividing the interval with the highest upper bound, and computing
|
||||
* bounds on the sub-intervals.
|
||||
* This process continues until (i) the maximum recursion depth is reached
|
||||
* or (ii) the difference between the maximum upper bound and maximum lower
|
||||
* bound is less than a certain tolerance (\p tol * [initial maximum
|
||||
* upper bound - initial maximum lower bound]).
|
||||
* The function also terminates if the highest maxima estimate is found
|
||||
* to be below the given threshold \p max_threshold. This is useful when
|
||||
* we are interested in computing the global maximum of the function
|
||||
* over all elements. In this case we can reject elements where the upper
|
||||
* bound is below the current global maximum. In case the function
|
||||
* maximum on the element is above the global maximum, we update
|
||||
* \p max_threshold.
|
||||
*
|
||||
* We return a pair of values that bracket the actual maximum, i.e.
|
||||
* [max_lower_bound, max_upper_bound].
|
||||
*/
|
||||
std::pair<real_t,real_t> EstimateFunctionMaximum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol,
|
||||
real_t &max_threshold)const;
|
||||
|
||||
public:
|
||||
/** @brief For each vdof, counts how many elements contain the vdof,
|
||||
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
|
||||
@@ -1662,21 +1726,21 @@ public:
|
||||
*/
|
||||
///@{
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in @b lower and @b upper. We also return the
|
||||
/// points based on \p ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in \p lower and \p upper. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// We compute the bounds for each vdim if \p vdim < 1.
|
||||
/// Note: For most cases, this method/interface will be sufficient.
|
||||
virtual PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
/// ordered byVDim:
|
||||
/// points based on \p ref_factor, and returns the bounds for each element
|
||||
/// ordered byNodes:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// We compute the bounds for each vdim if \p vdim < 1.
|
||||
PLBound GetElementBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
@@ -1687,6 +1751,18 @@ public:
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1) const;
|
||||
|
||||
/** @brief Gets the bounds on given reference range inside an element.
|
||||
*
|
||||
* @details @a ref_range is a vector of size 2*dim that specifies the
|
||||
* lower and upper limits in each dimension of the reference element.
|
||||
* For example, in 2D, ref_range = [rmin, smin, rmax, smax].
|
||||
*/
|
||||
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
|
||||
const Vector &ref_range,
|
||||
const int vdim,
|
||||
Vector &lower, Vector &upper,
|
||||
Vector &control_pos) const;
|
||||
|
||||
/// Compute bounds on the grid function for the given element.
|
||||
/// The bounds are stored in @b lower and @b upper.
|
||||
void GetElementBounds(const int elem, const PLBound &plb,
|
||||
@@ -1694,11 +1770,45 @@ public:
|
||||
const int vdim = -1) const;
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byVDim:
|
||||
/// are returned in @b lower and @b upper, ordered byNodes:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
const int vdim=-1) const;
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
|
||||
*
|
||||
* @details See the protected version of EstimateFunctionMinimum for
|
||||
* details.
|
||||
*/
|
||||
std::pair<real_t, real_t> EstimateFunctionMinimum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
|
||||
*
|
||||
* @details See the protected version of EstimateFunctionMaximum for
|
||||
* details.
|
||||
*/
|
||||
std::pair<real_t, real_t> EstimateFunctionMaximum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the GridFunction minimum across all elements. */
|
||||
virtual std::pair<real_t,real_t> EstimateFunctionMinimum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the GridFunction maximum across all elements. */
|
||||
virtual std::pair<real_t,real_t> EstimateFunctionMaximum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
///@}
|
||||
|
||||
/// Destroys grid function.
|
||||
|
||||
@@ -171,15 +171,15 @@ template<int DIM, int T_SDIM, int T_D1D, int T_Q1D>
|
||||
VectorDiffusionIntegrator::ApplyKernelType
|
||||
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if (DIM == 2)
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
|
||||
}
|
||||
else if (DIM == 3)
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
|
||||
}
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
}
|
||||
|
||||
inline VectorDiffusionIntegrator::ApplyKernelType
|
||||
|
||||
@@ -182,15 +182,15 @@ template<int DIM, int T_D1D, int T_Q1D>
|
||||
VectorMassIntegrator::VectorMassAddMultPAType
|
||||
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
|
||||
{
|
||||
if (DIM == 2)
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
|
||||
}
|
||||
else if (DIM == 3)
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
}
|
||||
|
||||
inline VectorMassIntegrator::VectorMassAddMultPAType
|
||||
|
||||
@@ -301,18 +301,14 @@ template <int DIM, int T_D1D, int T_Q1D>
|
||||
DomainLFIntegrator::AssembleKernelType
|
||||
DomainLFIntegrator::AssembleKernels::Kernel()
|
||||
{
|
||||
switch (DIM)
|
||||
{
|
||||
case 1:
|
||||
return DLFEvalAssemble1D<T_D1D, T_Q1D>;
|
||||
case 2:
|
||||
return DLFEvalAssemble2D<T_D1D, T_Q1D>;
|
||||
case 3:
|
||||
return DLFEvalAssemble3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
if constexpr (DIM == 1) { return DLFEvalAssemble1D<T_D1D, T_Q1D>; }
|
||||
if constexpr (DIM == 2) { return DLFEvalAssemble2D<T_D1D, T_Q1D>; }
|
||||
if constexpr (DIM == 3) { return DLFEvalAssemble3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
#endif
|
||||
|
||||
#endif // MFEM_LININTEG_DOMAIN_KERNELS_HPP
|
||||
|
||||
+6
-5
@@ -158,15 +158,16 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
|
||||
int i;
|
||||
i = dofmap_lor[off_lor + i1 + i2*2];
|
||||
int s1 = i < 0 ? -1 : 1;
|
||||
int idof_lor = vdof_lor[absdof(i)];
|
||||
int idof_lor = vdof_lor[UnsignIndex(i)];
|
||||
i = dofmap_ho[off_ho + i1*n1 + i2*n2];
|
||||
int s2 = i < 0 ? -1 : 1;
|
||||
int idof_ho = vdof_ho[absdof(i)];
|
||||
int idof_ho = vdof_ho[UnsignIndex(i)];
|
||||
int s3 = idof_lor < 0 ? -1 : 1;
|
||||
int s4 = idof_ho < 0 ? -1 : 1;
|
||||
int s = s1*s2*s3*s4;
|
||||
i = absdof(idof_ho);
|
||||
perm_[absdof(idof_lor)] = s < 0 ? -1-absdof(i) : absdof(i);
|
||||
i = UnsignIndex(idof_ho);
|
||||
perm_[UnsignIndex(idof_lor)] = s < 0 ? -1-UnsignIndex(i) :
|
||||
UnsignIndex(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -232,7 +233,7 @@ void LORBase::ConstructDofPermutation() const
|
||||
int j = l_perm[i];
|
||||
int s = j < 0 ? -1 : 1;
|
||||
int t_i = pfes_lor->GetLocalTDofNumber(i);
|
||||
int t_j = pfes_ho->GetLocalTDofNumber(absdof(j));
|
||||
int t_j = pfes_ho->GetLocalTDofNumber(UnsignIndex(j));
|
||||
// Either t_i and t_j both -1, or both non-negative
|
||||
if ((t_i < 0 && t_j >=0) || (t_j < 0 && t_i >= 0))
|
||||
{
|
||||
|
||||
@@ -57,8 +57,6 @@ private:
|
||||
/// values (after temporarily changing them for LOR assembly).
|
||||
void ResetIntegrationRules(GetIntegratorsFn get_integrators);
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
|
||||
protected:
|
||||
enum FESpaceType { H1, ND, RT, L2, INVALID };
|
||||
|
||||
|
||||
+41
-47
@@ -424,7 +424,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -462,7 +462,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -500,7 +500,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -538,16 +538,16 @@ void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
|
||||
{
|
||||
if (dofs[i] < 0)
|
||||
{
|
||||
if (ldof_sign[-1-dofs[i]] < 0)
|
||||
if (ldof_sign[FlipIndexSign(dofs[i])] < 0)
|
||||
{
|
||||
dofs[i] = -1-dofs[i];
|
||||
dofs[i] = FlipIndexSign(dofs[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ldof_sign[dofs[i]] < 0)
|
||||
{
|
||||
dofs[i] = -1-dofs[i];
|
||||
dofs[i] = FlipIndexSign(dofs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -699,7 +699,8 @@ void ParFiniteElementSpace::GetSharedEdgeDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
dofs[i] = di >= 0 ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,7 +724,8 @@ void ParFiniteElementSpace::GetSharedTriangleDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
dofs[i] = di >= 0 ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -747,7 +749,8 @@ void ParFiniteElementSpace::GetSharedQuadrilateralDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,7 +1490,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
int ldof = UnsignIndex(ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
{
|
||||
@@ -1548,7 +1551,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
int ldof = UnsignIndex(ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
{
|
||||
@@ -1573,14 +1576,15 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
|
||||
for (int i = 0; i < num_ldofs; i++)
|
||||
{
|
||||
int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
|
||||
int ldof = UnsignIndex(ldofs_fn[i]);
|
||||
ldof_marker[ldof] = i;
|
||||
}
|
||||
|
||||
for ( ; j < j_end; j++)
|
||||
{
|
||||
int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
|
||||
const int ldof = UnsignIndex(send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] :
|
||||
FlipIndexSign(ldof_marker[ldof]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1672,12 +1676,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
{
|
||||
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
|
||||
{
|
||||
int ldof = face_nbr_ldof.GetJ()[j];
|
||||
if (ldof < 0)
|
||||
{
|
||||
ldof = -1-ldof;
|
||||
}
|
||||
|
||||
const int ldof = UnsignIndex(face_nbr_ldof.GetJ()[j]);
|
||||
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
|
||||
}
|
||||
}
|
||||
@@ -1721,7 +1720,7 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
MFEM_ASSERT(Nonconforming() && i >= pmesh->GetNumFaces(), "");
|
||||
int el1, el2, inf1, inf2;
|
||||
pmesh->GetFaceElements(i, &el1, &el2);
|
||||
el2 = -1 - el2;
|
||||
el2 = FlipIndexSign(el2);
|
||||
pmesh->GetFaceInfos(i, &inf1, &inf2);
|
||||
MFEM_ASSERT(0 <= el2 && el2 < face_nbr_element_dof.Size(), "");
|
||||
const int nd = face_nbr_element_dof.RowSize(el2);
|
||||
@@ -1737,7 +1736,8 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
{
|
||||
const int ldof = vdofs[j];
|
||||
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
|
||||
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] :
|
||||
FlipIndexSign(vol_vdofs[FlipIndexSign(ldof)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2061,8 +2061,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
|
||||
|
||||
for (int j = 0; j < ne; j++)
|
||||
{
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
|
||||
/* */ : (-1 - (first + (-1 - ind[j])));
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
|
||||
FlipIndexSign(first + FlipIndexSign(ind[j]));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2072,8 +2072,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
|
||||
const int *ind = fec->DofOrderForOrientation(Geometry::SEGMENT, Eo[i]);
|
||||
for (int j = 0; j < ne; j++)
|
||||
{
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
|
||||
/* */ : (-1 - (first + (-1 - ind[j])));
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
|
||||
FlipIndexSign(first + FlipIndexSign(ind[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2866,7 +2866,7 @@ void NeighborRowMessage::Encode(int rank)
|
||||
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
edof = FlipIndexSign(edof);
|
||||
s = -1;
|
||||
}
|
||||
|
||||
@@ -3067,10 +3067,10 @@ void NeighborRowMessage::Decode(int rank)
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
real_t s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
edof = UnsignIndex(edof);
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
edof = FlipIndexSign(edof);
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
@@ -3121,10 +3121,10 @@ void NeighborRowMessage::Decode(int rank)
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
edof = UnsignIndex(edof);
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
edof = FlipIndexSign(edof);
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
@@ -4405,12 +4405,9 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
{
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
int row = DofToVDof(dofs[j], vd);
|
||||
if (row < 0) { row = -1 - row; }
|
||||
|
||||
int col = DofToVDof(old_dofs[j], vd, old_ndofs);
|
||||
if (col < 0) { col = -1 - col; }
|
||||
|
||||
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
|
||||
const int col = UnsignIndex(DofToVDof(old_dofs[j], vd,
|
||||
old_ndofs));
|
||||
i_diag[row] = col;
|
||||
}
|
||||
}
|
||||
@@ -4435,9 +4432,7 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
{
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
int row = DofToVDof(dofs[j], vd);
|
||||
if (row < 0) { row = -1 - row; }
|
||||
|
||||
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
|
||||
if (i_diag[row] == i_diag[row+1]) // diag row empty?
|
||||
{
|
||||
i_offd[row] = old_dofs[j + vd * dofs.Size()];
|
||||
@@ -4546,9 +4541,9 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
|
||||
int fine_rank = old_ranks[k];
|
||||
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
const int fine_rank = old_ranks[k];
|
||||
const int coarse_rank = (emb.parent < 0) ? FlipIndexSign(emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
|
||||
if (coarse_rank != MyRank && fine_rank == MyRank)
|
||||
{
|
||||
@@ -4636,8 +4631,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
@@ -4686,8 +4681,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
const int m = UnsignIndex(DofToVDof(dofs[i], vd));
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
|
||||
@@ -1568,6 +1568,39 @@ PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
return plb;
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMinimum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
std::pair<real_t, real_t> minmax =
|
||||
GridFunction::EstimateFunctionMinimum(vdim, plb, max_depth, tol);
|
||||
|
||||
real_t glob_min_lower = minmax.first;
|
||||
real_t glob_min_upper = minmax.second;
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_min_lower, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_min_upper, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
|
||||
return std::make_pair(glob_min_lower, glob_min_upper);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMaximum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
std::pair<real_t, real_t> minmax =
|
||||
GridFunction::EstimateFunctionMaximum(vdim, plb, max_depth, tol);
|
||||
|
||||
real_t glob_max_lower = minmax.first;
|
||||
real_t glob_max_upper = minmax.second;
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_max_lower, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_max_upper, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
return std::make_pair(glob_max_lower, glob_max_upper);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
@@ -609,6 +609,18 @@ public:
|
||||
PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const override;
|
||||
|
||||
/** @brief Estimate the GridFunction minimum across all elements. */
|
||||
std::pair<real_t, real_t> EstimateFunctionMinimum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const override;
|
||||
|
||||
/** @brief Estimate the GridFunction maximum across all elements. */
|
||||
std::pair<real_t, real_t> EstimateFunctionMaximum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const override;
|
||||
|
||||
/** Save the local portion of the ParGridFunction. This differs from the
|
||||
serial GridFunction::Save in that it takes into account the signs of
|
||||
the local dofs. */
|
||||
|
||||
+1
-4
@@ -271,10 +271,7 @@ inline void QuadratureFunction::GetValues(
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
// Make the values matrix memory an alias of the quadrature function memory
|
||||
Memory<real_t> &values_mem = values.GetMemory();
|
||||
values_mem.Delete();
|
||||
values_mem.MakeAlias(GetMemory(), vdim*s_offset, vdim*sl_size);
|
||||
values.SetSize(vdim, sl_size);
|
||||
values.MakeRef(GetMemory(), vdim*s_offset, vdim, sl_size);
|
||||
}
|
||||
|
||||
inline void QuadratureFunction::GetValues(
|
||||
|
||||
+8
-9
@@ -334,17 +334,16 @@ template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1)
|
||||
if constexpr (DIM == 1)
|
||||
{
|
||||
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
|
||||
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
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 (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(""); }
|
||||
else if constexpr (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -203,10 +203,10 @@ template<int DIM, QVectorLayout Q_LAYOUT,
|
||||
QuadratureInterpolator::TensorEvalKernelType
|
||||
QuadratureInterpolator::TensorEvalKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -453,8 +453,15 @@ QuadratureInterpolator::TensorEvalHDivKernels::Kernel()
|
||||
{
|
||||
using namespace internal::quadrature_interpolator;
|
||||
static_assert(DIM == 2 || DIM == 3, "only DIM=2 and DIM=3 are implemented!");
|
||||
if (DIM == 2) { return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>; }
|
||||
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
MFEM_ABORT("only DIM=2 and DIM=3 are implemented!");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -592,10 +592,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
QuadratureInterpolator::GradKernelType
|
||||
QuadratureInterpolator::GradKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
@@ -603,10 +603,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
QuadratureInterpolator::CollocatedGradKernelType
|
||||
QuadratureInterpolator::CollocatedGradKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -752,10 +752,10 @@ template <int DIM, int VDIM, int ND, int NQ>
|
||||
EvalKernel QuadratureInterpolator::EvalKernels::Kernel()
|
||||
{
|
||||
using namespace internal::quadrature_interpolator;
|
||||
if (DIM == 1) { return Eval1D; }
|
||||
else if (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
|
||||
else if (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
if constexpr (DIM == 1) { return Eval1D; }
|
||||
else if constexpr (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
|
||||
else if constexpr (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
template <int DIM>
|
||||
|
||||
+4
-6
@@ -844,8 +844,6 @@ void ConformingFaceRestriction::ComputeGatherIndices(
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
|
||||
void ConformingFaceRestriction::SetFaceDofsScatterIndices(
|
||||
const Mesh::FaceInformation &face,
|
||||
const int face_index,
|
||||
@@ -868,9 +866,9 @@ void ConformingFaceRestriction::SetFaceDofsScatterIndices(
|
||||
{
|
||||
const int lex_volume_dof = face_map[face_dof];
|
||||
const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof]; // signed
|
||||
const int volume_dof = absdof(s_volume_dof);
|
||||
const int volume_dof = UnsignIndex(s_volume_dof);
|
||||
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
|
||||
const int global_dof = absdof(s_global_dof);
|
||||
const int global_dof = UnsignIndex(s_global_dof);
|
||||
const int restriction_dof = face_dofs*face_index + face_dof;
|
||||
scatter_indices[restriction_dof] = s_global_dof;
|
||||
++gather_offsets[global_dof + 1];
|
||||
@@ -897,10 +895,10 @@ void ConformingFaceRestriction::SetFaceDofsGatherIndices(
|
||||
{
|
||||
const int lex_volume_dof = face_map[face_dof];
|
||||
const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof];
|
||||
const int volume_dof = absdof(s_volume_dof);
|
||||
const int volume_dof = UnsignIndex(s_volume_dof);
|
||||
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
|
||||
const int sgn = (s_global_dof >= 0) ? 1 : -1;
|
||||
const int global_dof = absdof(s_global_dof);
|
||||
const int global_dof = UnsignIndex(s_global_dof);
|
||||
const int restriction_dof = face_dofs*face_index + face_dof;
|
||||
const int s_restriction_dof = (sgn >= 0) ? restriction_dof : -1 -
|
||||
restriction_dof;
|
||||
|
||||
+5
-2
@@ -4102,8 +4102,11 @@ void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &d_loc,
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Don't count the overlapping DOFs in parallel.
|
||||
// The pfes might be ordered byVDIM, while the loop goes consecutively.
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
if (parallel)
|
||||
{
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
}
|
||||
#endif
|
||||
|
||||
dof_cnt++;
|
||||
|
||||
+24
-1
@@ -114,10 +114,22 @@ public:
|
||||
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
|
||||
|
||||
/// Move assignment operator
|
||||
/** If *this is a non-owning view (e.g., from MakeRef()), the data is copied
|
||||
so that the base is also modified. */
|
||||
Array<T> &operator=(Array<T> &&src)
|
||||
{
|
||||
if (this == &src) { return *this; }
|
||||
Swap(src); // Swap does not use move assignment!
|
||||
// If *this is a non-owning view (alias), and its capacity is sufficient
|
||||
// to contain src, then copy into *this so that the alias's base memory is
|
||||
// modified.
|
||||
if (!OwnsData() && Capacity() >= src.Size())
|
||||
{
|
||||
*this = src; // Copy assignment.
|
||||
}
|
||||
else
|
||||
{
|
||||
Swap(src); // Swap the pointers only.
|
||||
}
|
||||
src.DeleteAll();
|
||||
return *this;
|
||||
}
|
||||
@@ -251,6 +263,9 @@ public:
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
|
||||
/// Make this Array a reference to the given sub-Memory of @a base.
|
||||
inline void MakeRef(Memory<T> &base, int offset, int size_);
|
||||
|
||||
/// Reset the Array to use the given external Memory @a mem and size @a s.
|
||||
/** If @a own_mem is false, the Array will not own any of the pointers of
|
||||
@a mem.
|
||||
@@ -1073,6 +1088,14 @@ inline void Array<T>::MakeRef(const Array &master)
|
||||
data.MakeAlias(master.GetMemory(), 0, size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(Memory<T> &base, int offset, int size_)
|
||||
{
|
||||
data.Delete();
|
||||
size = size_;
|
||||
data.MakeAlias(base, offset, size_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::NewMemoryAndSize(
|
||||
const Memory<T> &mem, int s, bool own_mem)
|
||||
|
||||
@@ -113,6 +113,10 @@ void SetGlobalMPI_Comm(MPI_Comm comm);
|
||||
/// to suppress the warning.
|
||||
const char* GetEnv(const char* name);
|
||||
|
||||
/// Signed indices i -> -1 - i are used as a convention to encode orientation.
|
||||
inline MFEM_HOST_DEVICE int FlipIndexSign(int i) { return -1 - i; }
|
||||
inline MFEM_HOST_DEVICE int UnsignIndex(int i) { return i < 0 ? -1 - i : i; }
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
+19
-2
@@ -22,6 +22,14 @@
|
||||
//#define _WIN32
|
||||
//#define _aligned_malloc(s,a) malloc(s)
|
||||
|
||||
#ifdef NVTX_DEBUG_HPP
|
||||
#undef NVTX_COLOR
|
||||
#define NVTX_COLOR ::nvtx::kGold
|
||||
#include NVTX_DEBUG_HPP
|
||||
#else
|
||||
#define dbg(...)
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@@ -759,7 +767,14 @@ private:
|
||||
{
|
||||
switch (mt)
|
||||
{
|
||||
case MT::HOST_DEBUG: return new MmuHostMemorySpace();
|
||||
case MT::HOST_DEBUG:
|
||||
if (GetEnv("MFEM_MMU_STD"))
|
||||
{
|
||||
dbg("Using STD memory space for debug device!");
|
||||
return new StdHostMemorySpace();
|
||||
}
|
||||
dbg("Using MMU memory space for debug device!");
|
||||
return new MmuHostMemorySpace();
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
case MT::HOST_UMPIRE:
|
||||
return new UmpireHostMemorySpace(
|
||||
@@ -788,7 +803,9 @@ private:
|
||||
case MT::DEVICE_UMPIRE: return new NoDeviceMemorySpace();
|
||||
case MT::DEVICE_UMPIRE_2: return new NoDeviceMemorySpace();
|
||||
#endif
|
||||
case MT::DEVICE_DEBUG: return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE_DEBUG:
|
||||
if (GetEnv("MFEM_MMU_STD")) { return new StdDeviceMemorySpace(); }
|
||||
return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE:
|
||||
{
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
|
||||
@@ -200,6 +200,10 @@ void OptionsParser::Parse()
|
||||
isValid = isValidAsInt(argv[i]);
|
||||
*(int *)(options[j].var_ptr) = atoi(argv[i++]);
|
||||
break;
|
||||
case LONG:
|
||||
isValid = isValidAsInt(argv[i]);
|
||||
*(long long *)(options[j].var_ptr) = atoi(argv[i++]);
|
||||
break;
|
||||
case DOUBLE:
|
||||
isValid = isValidAsDouble(argv[i]);
|
||||
*(real_t *)(options[j].var_ptr) = atof(argv[i++]);
|
||||
@@ -278,6 +282,10 @@ void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
|
||||
case INT:
|
||||
os << *(int *)(opt.var_ptr);
|
||||
break;
|
||||
|
||||
case LONG:
|
||||
os << *(long long *)(opt.var_ptr);
|
||||
break;
|
||||
|
||||
case DOUBLE:
|
||||
os << *(real_t *)(opt.var_ptr);
|
||||
|
||||
@@ -31,7 +31,7 @@ class Vector;
|
||||
class OptionsParser
|
||||
{
|
||||
public:
|
||||
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR };
|
||||
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR , LONG };
|
||||
|
||||
private:
|
||||
struct Option
|
||||
@@ -98,6 +98,14 @@ public:
|
||||
required));
|
||||
}
|
||||
|
||||
/// Add a long integer option and set 'var' to receive the value.
|
||||
void AddOption(long long *var, const char *short_name, const char *long_name,
|
||||
const char *description, bool required = false)
|
||||
{
|
||||
options.Append(Option(LONG, var, short_name, long_name, description,
|
||||
required));
|
||||
}
|
||||
|
||||
/// Add a double option and set 'var' to receive the value.
|
||||
void AddOption(real_t *var, const char *short_name, const char *long_name,
|
||||
const char *description, bool required = false)
|
||||
|
||||
@@ -82,6 +82,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the DenseMatrix to reference the given sub-Memory of @a base.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the @a base Memory. */
|
||||
void MakeRef(Memory<real_t> &base, int offset, int h, int w)
|
||||
{
|
||||
data.MakeRef(base, offset, h*w);
|
||||
height = h; width = w;
|
||||
}
|
||||
|
||||
/// Change the data array and the size of the DenseMatrix.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the data array @a d. */
|
||||
|
||||
+58
-52
@@ -493,8 +493,7 @@ void Mesh::GetBdrElementTransformation(int i,
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
int idx = vdofs[n*k+j];
|
||||
pm(k,j) = nodes((idx<0)? -1-idx:idx);
|
||||
pm(k,j) = nodes(UnsignIndex(vdofs[n*k+j]));
|
||||
}
|
||||
}
|
||||
ElTr->SetFE(bdr_el);
|
||||
@@ -1356,7 +1355,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[0].conformity = ElementConformity::Coincident;
|
||||
face.element[1].conformity = ElementConformity::Coincident;
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].orientation = inf2%64;
|
||||
}
|
||||
}
|
||||
@@ -1379,7 +1378,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[0].conformity = ElementConformity::Coincident;
|
||||
face.element[1].conformity = ElementConformity::Superset;
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].orientation = inf2%64;
|
||||
}
|
||||
face.point_matrix = nc_faces_info[ncface].PointMatrix;
|
||||
@@ -1405,7 +1404,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[0].conformity = ElementConformity::Superset;
|
||||
face.element[1].conformity = ElementConformity::Coincident;
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].orientation = inf2%64;
|
||||
face.point_matrix = nc_faces_info[ncface].PointMatrix;
|
||||
}
|
||||
@@ -1438,7 +1437,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::SharedConforming:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -1448,7 +1447,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::SharedSlaveNonconforming:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -1456,7 +1455,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::GhostSlave:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -6514,7 +6513,7 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
|
||||
input >> edge_to_ukv[j] >> v[0] >> v[1];
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
edge_to_ukv[j] = -1 - edge_to_ukv[j];
|
||||
edge_to_ukv[j] = FlipIndexSign(edge_to_ukv[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6551,9 +6550,6 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
const int NP = NumOfElements; // number of patches
|
||||
const int NPKV = NP * dim; // number of patch knotvectors
|
||||
constexpr int notset = -9999999;
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
auto unSign = [](int i) { return (i < 0) ? -1 - i : i; };
|
||||
// Local edge index -> dimension convention
|
||||
auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
|
||||
|
||||
@@ -6569,7 +6565,7 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
{
|
||||
GetElementVertices(i, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : flipSign(i);
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : FlipIndexSign(i);
|
||||
ukv_to_rpkv[i] = i;
|
||||
}
|
||||
return;
|
||||
@@ -6619,14 +6615,14 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
// We've set this edge already - link this index to it
|
||||
if (edge_to_pkv[edge] != notset)
|
||||
{
|
||||
const int pkv_other = unSign(edge_to_pkv[edge]);
|
||||
const int pkv_other = UnsignIndex(edge_to_pkv[edge]);
|
||||
unite(pkv, pkv_other);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEdgeVertices(edge, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : flipSign(pkv);
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : FlipIndexSign(pkv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6653,10 +6649,10 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
edge_to_ukv.SetSize(NumOfEdges);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
const int pkv = unSign(edge_to_pkv[i]);
|
||||
const int pkv = UnsignIndex(edge_to_pkv[i]);
|
||||
const int rpkv = pkv_to_rpkv[pkv];
|
||||
const int ukv = rpkv_to_ukv[rpkv];
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? flipSign(ukv) : ukv;
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? FlipIndexSign(ukv) : ukv;
|
||||
}
|
||||
|
||||
CorrectPatchTopoOrientations(edge_to_ukv);
|
||||
@@ -6667,9 +6663,6 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
const int dim = Dimension(); // Topological (not physical) dimension
|
||||
if (dim == 1) { return; }
|
||||
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
|
||||
const Table *face2elem = GetFaceToElementTable();
|
||||
Array<int> pfaces, orient;
|
||||
Array<int> fe, feo;
|
||||
@@ -6688,7 +6681,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (auto e : fe)
|
||||
{
|
||||
const int skv = edge_to_ukv[e];
|
||||
if (skv == kv || flipSign(skv) == kv) { hasKV = true; }
|
||||
if (skv == kv || FlipIndexSign(skv) == kv) { hasKV = true; }
|
||||
}
|
||||
if (hasKV)
|
||||
{
|
||||
@@ -6718,7 +6711,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
};
|
||||
}
|
||||
|
||||
Array<int> ukvs((dim==2) ? 4 : 12);
|
||||
Array<int> ukvs((dim == 2) ? 4 : 12);
|
||||
Array<int> pe, oe;
|
||||
bool initKV = false;
|
||||
|
||||
@@ -6732,7 +6725,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (int i = 0; i < pe.Size(); i++)
|
||||
{
|
||||
ukvs[i] = edge_to_ukv[pe[i]];
|
||||
ukvs[i] = (oe[i] < 0) ? flipSign(ukvs[i]) : ukvs[i];
|
||||
ukvs[i] = (oe[i] < 0) ? FlipIndexSign(ukvs[i]) : ukvs[i];
|
||||
}
|
||||
|
||||
// Find the direction with this kv.
|
||||
@@ -6740,12 +6733,19 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (int d=0; d<dim; ++d) // Loop over directions.
|
||||
{
|
||||
const int skv = edge_to_ukv[pe[dir_edges[d][0]]];
|
||||
if (skv == kv || flipSign(skv) == kv)
|
||||
if (skv == kv || FlipIndexSign(skv) == kv)
|
||||
{
|
||||
thisDir = d;
|
||||
for (auto e : dir_edges[d])
|
||||
if (!edgeSet[pe[e]])
|
||||
{
|
||||
thisDir = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(thisDir >= 0, "");
|
||||
if (thisDir == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For this direction, find any edge already set. If no edge is set, we
|
||||
// arbitrarily take the first.
|
||||
@@ -6777,12 +6777,12 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
}
|
||||
|
||||
const int edge = pe[i];
|
||||
if ((dim == 2 && ukvs[i] != flipSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == flipSign(ukvs[ref_edge0])))
|
||||
if ((dim == 2 && ukvs[i] != FlipIndexSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == FlipIndexSign(ukvs[ref_edge0])))
|
||||
{
|
||||
// Flip the sign of this edge
|
||||
MFEM_VERIFY(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = flipSign(edge_to_ukv[edge]);
|
||||
MFEM_ASSERT(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = FlipIndexSign(edge_to_ukv[edge]);
|
||||
}
|
||||
|
||||
edgeSet[edge] = true;
|
||||
@@ -6827,10 +6827,11 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
int unsetDim = -1;
|
||||
for (int d=0; d<dim; ++d) // Loop over dimensions.
|
||||
{
|
||||
if (!edgeSet[pe[dir_edges[d][0]]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
for (auto e : dir_edges[d])
|
||||
if (!edgeSet[pe[e]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (unsetDim == -1)
|
||||
@@ -6839,9 +6840,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
continue;
|
||||
}
|
||||
|
||||
const int kv_signed = edge_to_ukv[pe[dir_edges[unsetDim][0]]];
|
||||
const int kv = kv_signed < 0 ? flipSign(kv_signed) : kv_signed;
|
||||
MFEM_VERIFY(!edgeSet[pe[dir_edges[unsetDim][0]]], "");
|
||||
const int kv = UnsignIndex(edge_to_ukv[pe[dir_edges[unsetDim][0]]]);
|
||||
|
||||
initKV = false;
|
||||
|
||||
@@ -6891,6 +6890,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
bool allSet = true;
|
||||
for (auto eset : edgeSet)
|
||||
{
|
||||
@@ -6899,7 +6899,8 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
allSet = false;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(allSet && unset.size() == 0, "Some edge is not set");
|
||||
MFEM_ASSERT(allSet && unset.size() == 0, "Some edge is not set");
|
||||
#endif
|
||||
|
||||
delete face2elem;
|
||||
}
|
||||
@@ -6941,7 +6942,7 @@ void Mesh::LoadNonconformingPatchTopo(std::istream &input,
|
||||
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
ukv = -1 - ukv;
|
||||
ukv = FlipIndexSign(ukv);
|
||||
}
|
||||
edge_to_ukv[j] = ukv;
|
||||
}
|
||||
@@ -12438,11 +12439,7 @@ void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
edge_vertex->GetRow(i, vert);
|
||||
int ki = e_to_k[i];
|
||||
if (ki < 0)
|
||||
{
|
||||
ki = -1 - ki;
|
||||
}
|
||||
const int ki = UnsignIndex(e_to_k[i]);
|
||||
|
||||
if (vmap)
|
||||
{
|
||||
@@ -15751,9 +15748,18 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
{
|
||||
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
|
||||
|
||||
auto factor = [&](int N)
|
||||
// Closest int divisor to the cubit root, going down.
|
||||
auto factor3 = [](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
|
||||
for (int i = static_cast<int>(round(cbrt(N))); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
|
||||
// Closest int divisor to the square root, going down.
|
||||
auto factor2 = [](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(round(sqrt(N))); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
@@ -15777,22 +15783,22 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
int el0_x, el0_y, el0_z;
|
||||
if (dim == 2)
|
||||
{
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_x = factor2(mpi_cnt);
|
||||
mpi_y = mpi_cnt / mpi_x;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_y = factor(el0);
|
||||
el0_y = factor2(el0);
|
||||
el0_x = el0 / el0_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_y = factor(mpi_cnt / mpi_x);
|
||||
mpi_x = factor3(mpi_cnt);
|
||||
mpi_y = factor2(mpi_cnt / mpi_x);
|
||||
mpi_z = mpi_cnt / mpi_x / mpi_y;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_z = factor(el0);
|
||||
el0_y = factor(el0 / el0_z);
|
||||
el0_z = factor3(el0);
|
||||
el0_y = factor2(el0 / el0_z);
|
||||
el0_x = el0 / el0_y / el0_z;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ int ThresholdRefiner::ApplyImpl(Mesh &mesh)
|
||||
if (num_marked_elements == 0LL) { return STOP; }
|
||||
|
||||
mesh.GeneralRefinement(marked_elements, non_conforming, nc_limit);
|
||||
return CONTINUE + REFINED;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
|
||||
}
|
||||
|
||||
void ThresholdRefiner::Reset()
|
||||
@@ -162,7 +162,7 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh)
|
||||
const Vector &local_err = estimator.GetLocalErrors();
|
||||
bool derefs = mesh.DerefineByError(local_err, threshold, nc_limit, op);
|
||||
|
||||
return derefs ? CONTINUE + DEREFINED : NONE;
|
||||
return derefs ? static_cast<int>(CONTINUE) + static_cast<int>(DEREFINED) : NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it)
|
||||
}
|
||||
delete l2fes;
|
||||
delete gf;
|
||||
return CONTINUE + REFINED;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
|
||||
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ int Rebalancer::ApplyImpl(Mesh &mesh)
|
||||
if (pmesh && pmesh->Nonconforming())
|
||||
{
|
||||
pmesh->Rebalance();
|
||||
return CONTINUE + REBALANCED;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REBALANCED);
|
||||
}
|
||||
#endif
|
||||
return NONE;
|
||||
|
||||
+2
-2
@@ -3542,7 +3542,7 @@ void NCMesh::TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
|
||||
|
||||
// create a slave face record with a degenerate point matrix
|
||||
face_list.slaves.Append(
|
||||
Slave(-1 - enode.edge_index,
|
||||
Slave(FlipIndexSign(enode.edge_index),
|
||||
eid[0].element, eid[0].local, Geometry::SQUARE));
|
||||
Slave &sl = face_list.slaves.Last();
|
||||
|
||||
@@ -3589,7 +3589,7 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
|
||||
// non-slave edge is really a (face-)slave itself.
|
||||
const MeshId &eid = *eid_and_type.id;
|
||||
face_list.slaves.Append(
|
||||
Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
|
||||
Slave(FlipIndexSign(eid.index), eid.element, eid.local, Geometry::TRIANGLE));
|
||||
|
||||
int v0index = nodes[vn0].vert_index;
|
||||
int v1index = nodes[vn1].vert_index;
|
||||
|
||||
+40
-44
@@ -93,7 +93,7 @@ void NCNURBSExtension::GetMasterEdgeEntities(
|
||||
}
|
||||
else
|
||||
{
|
||||
const int auxEdge = -1 - edge_i;
|
||||
const int auxEdge = FlipIndexSign(edge_i);
|
||||
GetAuxEdgeVertices(auxEdge, sverts);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
{
|
||||
if (edge < 0)
|
||||
{
|
||||
sideAuxEdges[s].Append(-1 - edge);
|
||||
sideAuxEdges[s].Append(FlipIndexSign(edge));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,7 +456,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
== afverts[j], "");
|
||||
}
|
||||
|
||||
ori_f2 = -1 - ori_f2;
|
||||
ori_f2 = FlipIndexSign(ori_f2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -468,7 +468,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
}
|
||||
|
||||
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
|
||||
SlaveFaceInfo{-1 - afid, ori_f2,
|
||||
SlaveFaceInfo{FlipIndexSign(afid), ori_f2,
|
||||
{fki(vMinID,0), fki(vMinID,1)},
|
||||
{
|
||||
fki((vMinID + 2) % 4,0) - fki(vMinID,0),
|
||||
@@ -509,7 +509,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
auxFaces.push_back(auxFace);
|
||||
|
||||
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
|
||||
SlaveFaceInfo{-1 - auxFaceId, ori_f,
|
||||
SlaveFaceInfo{FlipIndexSign(auxFaceId), ori_f,
|
||||
{fki(vMinID,0), fki(vMinID,1)},
|
||||
{
|
||||
fki((vMinID + 2) % 4,0) - fki(vMinID,0),
|
||||
@@ -622,7 +622,7 @@ void NCNURBSExtension::GetAuxFaceEdges(int auxFace, Array<int> &edges) const
|
||||
}
|
||||
else // Auxiliary edge
|
||||
{
|
||||
edges[i] = -1 - auxv2e.at(edge_v);
|
||||
edges[i] = FlipIndexSign(auxv2e.at(edge_v));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,7 +633,7 @@ int OffsetHelper(int i, int j, const Array<int> &a, const Array<int> &b)
|
||||
{
|
||||
if (i < 0)
|
||||
{
|
||||
return b[-1 - i + j];
|
||||
return b[FlipIndexSign(i) + j];
|
||||
}
|
||||
else if (i + j < a.Size())
|
||||
{
|
||||
@@ -679,7 +679,7 @@ void NCNURBSExtension::GetMasterEdgeDofs(bool dof, int me,
|
||||
}
|
||||
else // Auxiliary edge
|
||||
{
|
||||
GetAuxEdgeVertices(-1 - slaveId, svert);
|
||||
GetAuxEdgeVertices(FlipIndexSign(slaveId), svert);
|
||||
}
|
||||
|
||||
bool reverse = false;
|
||||
@@ -872,7 +872,7 @@ void ReorderArray2D(int i0, int j0, const Array2D<int> &a,
|
||||
// Set a quadrilateral vertex index permutation for a given orientation.
|
||||
void GetVertexOrdering(int ori, std::array<int, 4> &perm)
|
||||
{
|
||||
const int oriAbs = ori < 0 ? -1 - ori : ori;
|
||||
const int oriAbs = UnsignIndex(ori);
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
@@ -1094,7 +1094,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
if (slaveId < 0)
|
||||
{
|
||||
// Auxiliary face
|
||||
const int auxFace = -1 - slaveId;
|
||||
const int auxFace = FlipIndexSign(slaveId);
|
||||
|
||||
// Set slave face entity dimensions.
|
||||
if (dof)
|
||||
@@ -1171,7 +1171,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
}
|
||||
else
|
||||
{
|
||||
const int auxEdge = -1 - edge;
|
||||
const int auxEdge = FlipIndexSign(edge);
|
||||
GetAuxEdgeVertices(auxEdge, evert);
|
||||
}
|
||||
MFEM_ASSERT(evert[0] == vstart || evert[1] == vstart, "");
|
||||
@@ -1184,7 +1184,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
// dimensions of the master face, by using ori.
|
||||
int e1 = -1, e2 = -1;
|
||||
{
|
||||
const int aori = ori < 0 ? -1 - ori : ori;
|
||||
const int aori = UnsignIndex(ori);
|
||||
if (aori % 2 == 0)
|
||||
{
|
||||
e1 = 0;
|
||||
@@ -1416,14 +1416,15 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
|
||||
{
|
||||
// Create a new auxiliary edge
|
||||
auxv2e[childPair] = auxEdges.size();
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
|
||||
parentEdge : -1 - parentEdge,
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ? parentEdge :
|
||||
FlipIndexSign(parentEdge),
|
||||
{childPair.first, childPair.second},
|
||||
{newParentEdge ? 0 : prevKI, ks}});
|
||||
}
|
||||
}
|
||||
|
||||
const int childEdge = childPairTopo ? v2e[childPair] : -1 - auxv2e[childPair];
|
||||
const int childEdge = childPairTopo ? v2e[childPair] :
|
||||
FlipIndexSign(auxv2e[childPair]);
|
||||
|
||||
// Check whether this is the final vertex in this parent edge. Note that
|
||||
// the logic for comparing (pv[0],pv[1]) to the next parents assumes the
|
||||
@@ -1460,14 +1461,15 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
|
||||
|
||||
// -1 denotes `ne` at endpoint
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
|
||||
-1 - parentEdge : parentEdge,
|
||||
FlipIndexSign(parentEdge) :
|
||||
parentEdge,
|
||||
{finalChildPair.first, finalChildPair.second},
|
||||
{ks, -1}});
|
||||
}
|
||||
}
|
||||
|
||||
const int finalChildEdge = finalChildPairTopo ? v2e[finalChildPair] :
|
||||
-1 - auxv2e[finalChildPair];
|
||||
FlipIndexSign(auxv2e[finalChildPair]);
|
||||
edgePairs.emplace_back(-1, -1, finalChildEdge, parentEdge);
|
||||
}
|
||||
|
||||
@@ -1805,7 +1807,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
auxFaces.push_back(auxFace);
|
||||
facePairs.emplace_back(
|
||||
FacePairInfo{cv[0], parentFace,
|
||||
SlaveFaceInfo{-1 - auxv2f[childPair],
|
||||
SlaveFaceInfo{FlipIndexSign(auxv2f[childPair]),
|
||||
0, {i0, j0}, {d0, d1}}});
|
||||
}
|
||||
}
|
||||
@@ -2111,7 +2113,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
auxv2e[childPair] = auxEdges.size();
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv0 < pv1 ?
|
||||
parentEdge :
|
||||
-1 - parentEdge,
|
||||
FlipIndexSign(parentEdge),
|
||||
{childPair.first, childPair.second},
|
||||
{knotIndex0, knotIndex1}});
|
||||
}
|
||||
@@ -2131,7 +2133,8 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
|
||||
const EdgePairInfo ep_e((e_idx == n_d - de) ? -1 : tv,
|
||||
(e_idx == n_d - de) ? -1 : tvki,
|
||||
-1 - auxv2e[childPair], parentEdge);
|
||||
FlipIndexSign(auxv2e[childPair]),
|
||||
parentEdge);
|
||||
|
||||
const bool unset = !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
|
||||
if (unset)
|
||||
@@ -2226,7 +2229,7 @@ void NCNURBSExtension::GetAuxFaceToPatchTable(Array2D<int> &auxface2patch)
|
||||
if (s < 0)
|
||||
{
|
||||
// Auxiliary face.
|
||||
const int aux = -1 - s;
|
||||
const int aux = FlipIndexSign(s);
|
||||
if (auxface2patch(aux, 0) >= 0)
|
||||
{
|
||||
if (auxface2patch(aux, 1) != -1) { consistent = false; }
|
||||
@@ -2316,7 +2319,7 @@ void NCNURBSExtension::UpdateAuxiliaryKnotSpans(const Array<int> &rf)
|
||||
for (auto auxEdge : auxEdges)
|
||||
{
|
||||
const int p = auxEdge.parent;
|
||||
const int parent = p < 0 ? -1 - p : p;
|
||||
const int parent = UnsignIndex(p);
|
||||
const int kv = KnotInd(parent);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
@@ -2382,14 +2385,8 @@ int NCNURBSExtension::AuxiliaryEdgeNE(int aux_edge)
|
||||
const int signedParentEdge = auxEdges[aux_edge].parent;
|
||||
const int ki0 = auxEdges[aux_edge].ksi[0];
|
||||
const int ki1raw = auxEdges[aux_edge].ksi[1];
|
||||
int ki1 = ki1raw;
|
||||
if (ki1raw == -1)
|
||||
{
|
||||
const bool rev = signedParentEdge < 0;
|
||||
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
|
||||
ki1 = KnotVec(parentEdge)->GetNE();
|
||||
}
|
||||
|
||||
const int ki1 = ki1raw == -1 ? KnotVec(UnsignIndex(signedParentEdge))->GetNE()
|
||||
: ki1raw;
|
||||
return ki1 - ki0;
|
||||
}
|
||||
|
||||
@@ -2403,7 +2400,7 @@ void NCNURBSExtension::SlaveEdgeToParent(int se, int parent,
|
||||
Array<int> sev(2);
|
||||
if (se < 0) // Auxiliary edge
|
||||
{
|
||||
for (int i=0; i<2; ++i) { sev[i] = auxEdges[-1 - se].v[i]; }
|
||||
for (int i=0; i<2; ++i) { sev[i] = auxEdges[FlipIndexSign(se)].v[i]; }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2459,7 +2456,7 @@ void NCNURBSExtension::GetMasterEdgePieceOffsets(int mid, Array<int> &os)
|
||||
}
|
||||
else
|
||||
{
|
||||
nes = AuxiliaryEdgeNE(-1 - s);
|
||||
nes = AuxiliaryEdgeNE(FlipIndexSign(s));
|
||||
}
|
||||
|
||||
os[i+1] = os[i] + nes;
|
||||
@@ -2565,7 +2562,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
|
||||
}
|
||||
else // Aux edge
|
||||
{
|
||||
const int aux_edge = -1 - s;
|
||||
const int aux_edge = FlipIndexSign(s);
|
||||
if (auxef[aux_edge].Size() == 0)
|
||||
{
|
||||
auxef[aux_edge].SetSize(AuxiliaryEdgeNE(aux_edge));
|
||||
@@ -2611,7 +2608,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
|
||||
}
|
||||
|
||||
MFEM_VERIFY(consistent, "");
|
||||
return partialChange ? -1 - dirSet : dirSet;
|
||||
return partialChange ? FlipIndexSign(dirSet) : dirSet;
|
||||
}
|
||||
|
||||
void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
@@ -2715,7 +2712,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
if (s < 0)
|
||||
{
|
||||
// Auxiliary face.
|
||||
const int aux = -1 - s;
|
||||
const int aux = FlipIndexSign(s);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
const int patch = auxface2patch(aux, i);
|
||||
@@ -2763,7 +2760,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
|
||||
const int dirSetSigned = SetPatchFactors(p);
|
||||
const bool partialChange = dirSetSigned < 0;
|
||||
const int dirSet = partialChange ? -1 - dirSetSigned : dirSetSigned;
|
||||
const int dirSet = UnsignIndex(dirSetSigned);
|
||||
const bool changed = (patchState[p] != dirSet) || partialChange;
|
||||
patchState[p] = dirSet;
|
||||
|
||||
@@ -2806,8 +2803,8 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
{
|
||||
const int dirSetSigned_i = SetPatchFactors(i);
|
||||
const bool partialChange_i = dirSetSigned_i < 0;
|
||||
const int dirSet_i = partialChange_i ? -1 - dirSetSigned_i :
|
||||
dirSetSigned_i;
|
||||
const int dirSet_i = partialChange_i ?
|
||||
FlipIndexSign(dirSetSigned_i) : dirSetSigned_i;
|
||||
const bool changed_i = (patchState[i] != dirSet_i) ||
|
||||
partialChange_i;
|
||||
patchState[p] = dirSet_i;
|
||||
@@ -3027,7 +3024,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
|
||||
|
||||
// Check whether ordering is reversed.
|
||||
const bool rev = verts[(s + 1) % 4] != fverts[1];
|
||||
if (rev) { s = -1 - s; } // Reversed order is encoded by the sign.
|
||||
if (rev) { s = FlipIndexSign(s); } // Reversed order is encoded by the sign.
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -3040,7 +3037,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
|
||||
// see GetFaceOrientation.
|
||||
bool Reorder2D(int ori, std::array<int, 2> &s0)
|
||||
{
|
||||
const int shift = ori < 0 ? -1 - ori : ori;
|
||||
const int shift = UnsignIndex(ori);
|
||||
|
||||
// Shift is an F1 index in the counter-clockwise ordering of 4 quad vertices.
|
||||
// Now find the (i,j) indices of this index, with i,j in {0,1}.
|
||||
@@ -3064,7 +3061,7 @@ void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
|
||||
int &n)
|
||||
{
|
||||
const bool rev = (signedShift < 0);
|
||||
const int shift = rev ? -1 - signedShift : signedShift;
|
||||
const int shift = UnsignIndex(signedShift);
|
||||
MFEM_ASSERT(0 <= shift && shift < 4, "");
|
||||
|
||||
// We consider 8 cases for the possible values of rev and shift.
|
||||
@@ -3136,7 +3133,7 @@ void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift,
|
||||
int& sm, int& sn, int& si, int& sj)
|
||||
{
|
||||
const bool rev = (signedShift < 0);
|
||||
const int shift = rev ? -1 - signedShift : signedShift;
|
||||
const int shift = UnsignIndex(signedShift);
|
||||
MFEM_ASSERT(0 <= shift && shift < 4, "");
|
||||
|
||||
// (0,0) <= (i,j) < (m,n) are old indices, and old vertex [shift] maps
|
||||
@@ -3798,8 +3795,7 @@ void NCNURBSExtension::GenerateOffsets()
|
||||
const int signedParentEdge = auxEdges[e].parent;
|
||||
const int ki0 = auxEdges[e].ksi[0];
|
||||
const int ki1raw = auxEdges[e].ksi[1];
|
||||
const bool rev = signedParentEdge < 0;
|
||||
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
|
||||
const int parentEdge = UnsignIndex(signedParentEdge);
|
||||
const int masterNE = KnotVec(parentEdge)->GetNE();
|
||||
const int ki1 = ki1raw == -1 ? masterNE : ki1raw;
|
||||
const int perEdgeCP = GetNCPperEdge(KnotVec(e));
|
||||
|
||||
+11
-15
@@ -3723,10 +3723,7 @@ bool NURBSExtension::CheckPatches()
|
||||
for (int i = 0; i < edges.Size(); i++)
|
||||
{
|
||||
edges[i] = edge_to_ukv[edges[i]];
|
||||
if (oedge[i] < 0)
|
||||
{
|
||||
edges[i] = -1 - edges[i];
|
||||
}
|
||||
if (oedge[i] < 0) { edges[i] = FlipIndexSign(edges[i]); }
|
||||
}
|
||||
|
||||
// In 2d - opposite edges must be same knotvector with opposite sign.
|
||||
@@ -3740,7 +3737,7 @@ bool NURBSExtension::CheckPatches()
|
||||
// {7, 6}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7} for Geometry::CUBE in 3D
|
||||
// See fem/geom.cpp for these definitions.
|
||||
if ((dim == 2 &&
|
||||
(edges[0] != -1 - edges[2] || edges[1] != -1 - edges[3])) ||
|
||||
(edges[0] != FlipIndexSign(edges[2]) || edges[1] != FlipIndexSign(edges[3]))) ||
|
||||
|
||||
(dim == 3 &&
|
||||
(edges[0] != edges[2] || edges[0] != edges[4] ||
|
||||
@@ -3769,7 +3766,7 @@ void NURBSExtension::CheckBdrPatches()
|
||||
edges[i] = edge_to_ukv[edges[i]];
|
||||
if (oedge[i] < 0)
|
||||
{
|
||||
edges[i] = -1 - edges[i];
|
||||
edges[i] = FlipIndexSign(edges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4766,14 +4763,13 @@ void NURBSExtension::GenerateBdrElementDofTable()
|
||||
SetPatchToBdrElements();
|
||||
|
||||
int *dof = bel_dof->GetJ();
|
||||
int ndof = bel_dof->Size_of_connections();
|
||||
const int ndof = bel_dof->Size_of_connections();
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
int idx = dof[i];
|
||||
const int idx = dof[i];
|
||||
if (idx < 0)
|
||||
{
|
||||
dof[i] = -1 - (activeDof[-1-idx] - 1);
|
||||
dof[i] = -activeDof[-1-idx];
|
||||
dof[i] = -activeDof[FlipIndexSign(idx)];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4858,12 +4854,12 @@ void NURBSExtension::Generate2DBdrElementDofTable()
|
||||
for (int ii = 0; ii <= ord0; ii++)
|
||||
{
|
||||
conn.to = DofMap(p2g[(okv[0] >= 0) ? (i+ii) : (nx-i-ii)]);
|
||||
if (s == -1) { conn.to = -1 -conn.to; }
|
||||
if (s == -1) { conn.to = FlipIndexSign(conn.to); }
|
||||
bel_dof_list.Append(conn);
|
||||
}
|
||||
}
|
||||
bel_to_patch[lbe] = b;
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
|
||||
lbe++;
|
||||
}
|
||||
gbe++;
|
||||
@@ -4936,14 +4932,14 @@ void NURBSExtension::Generate3DBdrElementDofTable()
|
||||
{
|
||||
const int ii_ = (okv[0] >= 0) ? (i+ii) : (nx-i-ii);
|
||||
conn.to = DofMap(p2g(ii_, jj_));
|
||||
if (s == -1) { conn.to = -1 -conn.to; }
|
||||
if (s == -1) { conn.to = FlipIndexSign(conn.to); }
|
||||
bel_dof_list.Append(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
bel_to_patch[lbe] = b;
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
|
||||
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : (-1-j);
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
|
||||
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : FlipIndexSign(j);
|
||||
lbe++;
|
||||
}
|
||||
gbe++;
|
||||
|
||||
+3
-4
@@ -220,7 +220,7 @@ public:
|
||||
@a u.
|
||||
The main purpose of this function is its use in FindInterpolant.
|
||||
Use GetBotella instead for each shape function separately, perhaps in
|
||||
conjuction with GetSpan and GetRefPoint.*/
|
||||
conjunction with GetSpan and GetRefPoint.*/
|
||||
MFEM_DEPRECATED void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
|
||||
|
||||
/** @brief Global curve interpolation through the points @a x (overwritten).
|
||||
@@ -1398,8 +1398,7 @@ inline const real_t &NURBSPatch::operator()(int i, int j, int k, int l) const
|
||||
|
||||
inline int NURBSExtension::KnotInd(int edge) const
|
||||
{
|
||||
const int kv = edge_to_ukv[edge];
|
||||
return kv >= 0 ? kv : -1 - kv;
|
||||
return UnsignIndex(edge_to_ukv[edge]);
|
||||
}
|
||||
|
||||
inline int NURBSExtension::KnotSign(int edge) const
|
||||
@@ -1429,7 +1428,7 @@ const
|
||||
else
|
||||
{
|
||||
*okv = -oedge;
|
||||
return knotVectors[-1-kv];
|
||||
return knotVectors[FlipIndexSign(kv)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -400,7 +400,7 @@ void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
|
||||
}
|
||||
else // special case: prism edge-face constraint
|
||||
{
|
||||
if (entity_owner[1][-1-si] != MyRank)
|
||||
if (entity_owner[1][FlipIndexSign(si)] != MyRank)
|
||||
{
|
||||
master_flag |= 0x2;
|
||||
}
|
||||
@@ -571,9 +571,10 @@ void ParNCMesh::CalculatePMatrixGroups()
|
||||
ranks.SetSize(0);
|
||||
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
|
||||
{
|
||||
int si = face_list.slaves[j].index;
|
||||
int owner = (si >= 0) ? entity_owner[2][si] // standard face dependency
|
||||
/* */ : entity_owner[1][-1 - si]; // prism edge-face dep
|
||||
const int si = face_list.slaves[j].index;
|
||||
const int owner =
|
||||
(si >= 0) ? entity_owner[2][si] : // standard face dependency
|
||||
entity_owner[1][FlipIndexSign(si)]; // prism edge-face dep
|
||||
ranks.Append(groups[owner][0]);
|
||||
}
|
||||
ranks.Sort();
|
||||
@@ -1181,7 +1182,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
|
||||
|
||||
Mesh::FaceInfo &fi = pmesh.faces_info[cf.index];
|
||||
fi.Elem2No = -1 - fnbr_index[e[0]->index - NElements];
|
||||
fi.Elem2No = FlipIndexSign(fnbr_index[e[0]->index - NElements]);
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
@@ -1270,7 +1271,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
// In other words, side 1 IS the side that generated the face.
|
||||
}
|
||||
MFEM_ASSERT(fi.Elem2No >= NElements, "");
|
||||
fi.Elem2No = -1 - fnbr_index[fi.Elem2No - NElements];
|
||||
fi.Elem2No = FlipIndexSign(fnbr_index[fi.Elem2No - NElements]);
|
||||
|
||||
const DenseMatrix* pm = full_list.point_matrices[sf.geom][sf.matrix];
|
||||
if (!sloc && Dim == 3)
|
||||
@@ -2286,7 +2287,7 @@ void ParNCMesh::Derefine(const Array<int> &derefs)
|
||||
if (element_type[index] == 0)
|
||||
{
|
||||
// this coarse element will get pruned, encode who owns it now
|
||||
index = -1 - elements[coarse[i]].rank;
|
||||
index = FlipIndexSign(elements[coarse[i]].rank);
|
||||
}
|
||||
transforms.embeddings[i].parent = index;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ add_subdirectory(hdiv-linear-solver)
|
||||
add_subdirectory(hooke)
|
||||
add_subdirectory(meshing)
|
||||
add_subdirectory(mtop)
|
||||
add_subdirectory(mtop/chpt)
|
||||
add_subdirectory(mtop/examples)
|
||||
add_subdirectory(multidomain)
|
||||
add_subdirectory(nurbs)
|
||||
add_subdirectory(parelag)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
if(MFEM_USE_MPI)
|
||||
|
||||
list(APPEND AN_SOURCES linear_anisotropic_elasticity.hpp)
|
||||
list(APPEND AN_HEADERS linear_anisotropic_elasticity.cpp)
|
||||
|
||||
convert_filenames_to_full_paths(AN_SOURCES)
|
||||
convert_filenames_to_full_paths(AN_HEADERS)
|
||||
|
||||
set(EX_COMMON_FILES
|
||||
EXTRA_SOURCES ${AN_SOURCES}
|
||||
EXTRA_HEADERS ${AN_HEADERS})
|
||||
|
||||
add_mfem_miniapp(mtop_test_anisotropic_le
|
||||
MAIN test_anisotropic_le.cpp
|
||||
${EX_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
|
||||
endif (MFEM_USE_MPI)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
#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 (Wang–Moin–Iaccarino 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
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
#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
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
#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
|
||||
|
||||
@@ -0,0 +1,694 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
#ifndef MFEM_REVOLVE_CHECKPOINTING_HPP
|
||||
#define MFEM_REVOLVE_CHECKPOINTING_HPP
|
||||
|
||||
#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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //MFEM_REVOLVE_CHECKPOINTING_HPP
|
||||
@@ -0,0 +1,357 @@
|
||||
#ifndef MFEM_REVOLVE_SEPARATE_FILE_STORAGE_HPP
|
||||
#define MFEM_REVOLVE_SEPARATE_FILE_STORAGE_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio> // std::remove
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#include <filesystem>
|
||||
#include <system_error>
|
||||
#else
|
||||
#if defined(_WIN32)
|
||||
#include <direct.h> // _mkdir
|
||||
#else
|
||||
#include <sys/stat.h> // mkdir
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Separate-file checkpoint storage for REVOLVE: one file per checkpoint slot.
|
||||
*
|
||||
* This storage is "slot indexed":
|
||||
* slot = 0..max_slots-1
|
||||
*
|
||||
* File naming:
|
||||
* <dir>/<prefix><slot as zero-padded integer><ext>
|
||||
*
|
||||
* Example:
|
||||
* dir="ckpt", prefix="rev_", ext=".bin", max_slots=8
|
||||
* -> ckpt/rev_00.bin ... ckpt/rev_07.bin
|
||||
*
|
||||
* Intended REVOLVE interface:
|
||||
* int MaxSlots() const;
|
||||
* size_t SlotBytes() const;
|
||||
* void Save(int slot, const uint8_t *src, size_t bytes);
|
||||
* void Load(int slot, uint8_t *dst, size_t bytes) const;
|
||||
*
|
||||
* Notes:
|
||||
* - Slot files are pre-created and resized on construction if truncate_files=true.
|
||||
* - Save() overwrites the full slot file content from offset 0.
|
||||
* - Load() reads exactly SlotBytes() from offset 0.
|
||||
* - If keep_files=false, destructor deletes the slot files.
|
||||
* - If keep_open=true, keeps N files open (faster, but uses file descriptors).
|
||||
*/
|
||||
class RevolveSeparateFileCheckpointStorage
|
||||
{
|
||||
public:
|
||||
RevolveSeparateFileCheckpointStorage(const std::string &directory,
|
||||
const std::string &prefix,
|
||||
const std::string &ext,
|
||||
int max_slots,
|
||||
std::size_t slot_bytes,
|
||||
bool create_dir = true,
|
||||
bool truncate_files = true,
|
||||
bool keep_files = true,
|
||||
bool keep_open = false,
|
||||
bool flush_on_save = false)
|
||||
: dir_(directory),
|
||||
prefix_(prefix),
|
||||
ext_(ext),
|
||||
max_slots_(max_slots),
|
||||
slot_bytes_(slot_bytes),
|
||||
keep_files_(keep_files),
|
||||
keep_open_(keep_open),
|
||||
flush_on_save_(flush_on_save)
|
||||
{
|
||||
MFEM_VERIFY(max_slots_ > 0, "RevolveSeparateFileCheckpointStorage: max_slots must be > 0.");
|
||||
MFEM_VERIFY(slot_bytes_ > 0, "RevolveSeparateFileCheckpointStorage: slot_bytes must be > 0.");
|
||||
MFEM_VERIFY(!prefix_.empty(), "RevolveSeparateFileCheckpointStorage: prefix must not be empty.");
|
||||
|
||||
if (create_dir) { EnsureDirectory_(dir_); }
|
||||
|
||||
// Precompute slot paths.
|
||||
slot_paths_.resize((std::size_t)max_slots_);
|
||||
for (int s = 0; s < max_slots_; ++s)
|
||||
{
|
||||
slot_paths_[(std::size_t)s] = MakeSlotPath_(s);
|
||||
}
|
||||
|
||||
if (truncate_files)
|
||||
{
|
||||
PrecreateAll_();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Optional light sanity check: try opening one file. (User may want to reuse existing.)
|
||||
// We'll rely on Save/Load verification otherwise.
|
||||
}
|
||||
|
||||
if (keep_open_)
|
||||
{
|
||||
OpenAll_();
|
||||
}
|
||||
}
|
||||
|
||||
~RevolveSeparateFileCheckpointStorage()
|
||||
{
|
||||
CloseAll_();
|
||||
|
||||
if (!keep_files_)
|
||||
{
|
||||
RemoveAllFiles_();
|
||||
}
|
||||
}
|
||||
|
||||
int MaxSlots() const { return max_slots_; }
|
||||
std::size_t SlotBytes() const { return slot_bytes_; }
|
||||
|
||||
/// Return the full path for a given slot (useful for debugging).
|
||||
const std::string &SlotPath(int slot) const
|
||||
{
|
||||
MFEM_VERIFY(0 <= slot && slot < max_slots_, "SlotPath: slot out of range.");
|
||||
return slot_paths_[(std::size_t)slot];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save a checkpoint image into slot file.
|
||||
*
|
||||
* Requirements:
|
||||
* - slot in [0, MaxSlots())
|
||||
* - bytes == SlotBytes()
|
||||
* - src != nullptr
|
||||
*/
|
||||
void Save(int slot, const std::uint8_t *src, std::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.");
|
||||
|
||||
if (keep_open_)
|
||||
{
|
||||
std::fstream &f = files_[(std::size_t)slot];
|
||||
MFEM_VERIFY(f.is_open(), "Save: file not open (keep_open).");
|
||||
|
||||
f.clear();
|
||||
f.seekp(0, std::ios::beg);
|
||||
MFEM_VERIFY(f.good(), "Save: seekp failed (keep_open).");
|
||||
|
||||
f.write(reinterpret_cast<const char*>(src), (std::streamsize)slot_bytes_);
|
||||
MFEM_VERIFY(f.good(), "Save: write failed (keep_open).");
|
||||
|
||||
if (flush_on_save_) { f.flush(); }
|
||||
return;
|
||||
}
|
||||
|
||||
// Open on demand
|
||||
std::fstream f(SlotPath(slot).c_str(),
|
||||
std::ios::binary | std::ios::in | std::ios::out);
|
||||
if (!f.is_open())
|
||||
{
|
||||
// If missing, create it sized correctly, then reopen.
|
||||
PrecreateOne_(slot);
|
||||
f.open(SlotPath(slot).c_str(), std::ios::binary | std::ios::in | std::ios::out);
|
||||
}
|
||||
MFEM_VERIFY(f.is_open(), "Save: failed to open slot file.");
|
||||
|
||||
f.seekp(0, std::ios::beg);
|
||||
MFEM_VERIFY(f.good(), "Save: seekp failed.");
|
||||
|
||||
f.write(reinterpret_cast<const char*>(src), (std::streamsize)slot_bytes_);
|
||||
MFEM_VERIFY(f.good(), "Save: write failed.");
|
||||
|
||||
if (flush_on_save_) { f.flush(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load a checkpoint image from slot file.
|
||||
*
|
||||
* Requirements:
|
||||
* - slot in [0, MaxSlots())
|
||||
* - bytes == SlotBytes()
|
||||
* - dst != nullptr
|
||||
*/
|
||||
void Load(int slot, std::uint8_t *dst, std::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.");
|
||||
|
||||
if (keep_open_)
|
||||
{
|
||||
std::fstream &f = files_[(std::size_t)slot];
|
||||
MFEM_VERIFY(f.is_open(), "Load: file not open (keep_open).");
|
||||
|
||||
f.clear();
|
||||
f.seekg(0, std::ios::beg);
|
||||
MFEM_VERIFY(f.good(), "Load: seekg failed (keep_open).");
|
||||
|
||||
f.read(reinterpret_cast<char*>(dst), (std::streamsize)slot_bytes_);
|
||||
MFEM_VERIFY(f.good(), "Load: read failed (keep_open).");
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream f(SlotPath(slot).c_str(), std::ios::binary);
|
||||
MFEM_VERIFY(f.is_open(), "Load: failed to open slot file.");
|
||||
|
||||
f.read(reinterpret_cast<char*>(dst), (std::streamsize)slot_bytes_);
|
||||
MFEM_VERIFY(f.good(), "Load: read failed.");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string dir_;
|
||||
std::string prefix_;
|
||||
std::string ext_;
|
||||
int max_slots_ = 0;
|
||||
std::size_t slot_bytes_ = 0;
|
||||
|
||||
bool keep_files_ = true;
|
||||
bool keep_open_ = false;
|
||||
bool flush_on_save_ = false;
|
||||
|
||||
std::vector<std::string> slot_paths_;
|
||||
mutable std::vector<std::fstream> files_; // only used if keep_open_==true
|
||||
|
||||
static std::string JoinPath_(const std::string &dir, const std::string &file)
|
||||
{
|
||||
if (dir.empty()) { return file; }
|
||||
const char last = dir.back();
|
||||
if (last == '/' || last == '\\') { return dir + file; }
|
||||
return dir + "/" + file;
|
||||
}
|
||||
|
||||
int SlotDigits_() const
|
||||
{
|
||||
int x = max_slots_ - 1;
|
||||
int d = 1;
|
||||
while (x >= 10) { x /= 10; ++d; }
|
||||
return d;
|
||||
}
|
||||
|
||||
std::string MakeSlotFilename_(int slot) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << prefix_
|
||||
<< std::setw(SlotDigits_()) << std::setfill('0') << slot
|
||||
<< ext_;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string MakeSlotPath_(int slot) const
|
||||
{
|
||||
return JoinPath_(dir_, MakeSlotFilename_(slot));
|
||||
}
|
||||
|
||||
void PrecreateOne_(int slot) const
|
||||
{
|
||||
MFEM_VERIFY(0 <= slot && slot < max_slots_, "PrecreateOne: slot out of range.");
|
||||
|
||||
// Create/truncate and set file size to slot_bytes_ by writing last byte.
|
||||
std::ofstream ofs(SlotPath(slot).c_str(), std::ios::binary | std::ios::trunc);
|
||||
MFEM_VERIFY(ofs.is_open(), "PrecreateOne: failed to create slot file.");
|
||||
|
||||
const std::uint64_t sb = (std::uint64_t)slot_bytes_;
|
||||
MFEM_VERIFY(sb <= (std::uint64_t)std::numeric_limits<std::streamoff>::max(),
|
||||
"PrecreateOne: slot_bytes too large for streamoff.");
|
||||
|
||||
if (sb > 0)
|
||||
{
|
||||
ofs.seekp((std::streamoff)(sb - 1), std::ios::beg);
|
||||
MFEM_VERIFY(ofs.good(), "PrecreateOne: seekp failed.");
|
||||
|
||||
const char zero = 0;
|
||||
ofs.write(&zero, 1);
|
||||
MFEM_VERIFY(ofs.good(), "PrecreateOne: size write failed.");
|
||||
}
|
||||
}
|
||||
|
||||
void PrecreateAll_() const
|
||||
{
|
||||
for (int slot = 0; slot < max_slots_; ++slot)
|
||||
{
|
||||
PrecreateOne_(slot);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAll_()
|
||||
{
|
||||
files_.resize((std::size_t)max_slots_);
|
||||
|
||||
for (int slot = 0; slot < max_slots_; ++slot)
|
||||
{
|
||||
std::fstream &f = files_[(std::size_t)slot];
|
||||
f.open(SlotPath(slot).c_str(), std::ios::binary | std::ios::in | std::ios::out);
|
||||
if (!f.is_open())
|
||||
{
|
||||
PrecreateOne_(slot);
|
||||
f.open(SlotPath(slot).c_str(), std::ios::binary | std::ios::in | std::ios::out);
|
||||
}
|
||||
MFEM_VERIFY(f.is_open(), "OpenAll: failed to open slot file.");
|
||||
}
|
||||
}
|
||||
|
||||
void CloseAll_()
|
||||
{
|
||||
if (!files_.empty())
|
||||
{
|
||||
for (auto &f : files_) { if (f.is_open()) { f.close(); } }
|
||||
files_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveAllFiles_() const
|
||||
{
|
||||
for (int slot = 0; slot < max_slots_; ++slot)
|
||||
{
|
||||
// Ignore remove errors (e.g., already removed), but you can tighten if desired.
|
||||
std::remove(SlotPath(slot).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void EnsureDirectory_(const std::string &dir)
|
||||
{
|
||||
if (dir.empty()) { return; }
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
namespace fs = std::filesystem;
|
||||
std::error_code ec;
|
||||
|
||||
if (!fs::exists(dir, ec))
|
||||
{
|
||||
fs::create_directories(dir, ec);
|
||||
}
|
||||
MFEM_VERIFY(!ec, "EnsureDirectory: failed to create directory: " + dir);
|
||||
#else
|
||||
#if defined(_WIN32)
|
||||
const int rc = _mkdir(dir.c_str());
|
||||
if (rc != 0)
|
||||
{
|
||||
// If directory already exists, _mkdir fails. We accept that.
|
||||
// There's no reliable portable "exists" check in pre-C++17 without more code.
|
||||
}
|
||||
#else
|
||||
const int rc = mkdir(dir.c_str(), 0755);
|
||||
if (rc != 0 && errno != EEXIST)
|
||||
{
|
||||
MFEM_ABORT("EnsureDirectory: failed to create directory: " + dir);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_REVOLVE_SEPARATE_FILE_STORAGE_HPP
|
||||
@@ -0,0 +1,273 @@
|
||||
#ifndef MFEM_SEGMENT_CHECKPOINT_STORAGE_HPP
|
||||
#define MFEM_SEGMENT_CHECKPOINT_STORAGE_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "file_checkpoint_storage.hpp" // reuses DefaultCheckpointBinaryIO
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
|
||||
#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 Segment/range file storage: one file per handle-id range + in-file offsets.
|
||||
*
|
||||
* Segment file = <dir>/<prefix><segment_id><ext>
|
||||
* Where segment_id = handle / records_per_file.
|
||||
*
|
||||
* Pros:
|
||||
* - Much fewer files than file-per-snapshot
|
||||
* - No per-snapshot file create/delete
|
||||
*
|
||||
* Cons:
|
||||
* - Append-only: Erase() does not reclaim file space (records remain)
|
||||
* - Index is in-memory (not restartable across processes unless you persist it)
|
||||
*/
|
||||
template <typename Snapshot,
|
||||
typename SnapshotIO = DefaultCheckpointBinaryIO<Snapshot> >
|
||||
class SegmentedFileCheckpointStorage
|
||||
{
|
||||
public:
|
||||
using Handle = std::int64_t;
|
||||
|
||||
SegmentedFileCheckpointStorage(const std::string &directory,
|
||||
std::int64_t records_per_file = 4096,
|
||||
const std::string &prefix = "seg_",
|
||||
const std::string &extension = ".bin",
|
||||
bool create_dir = true,
|
||||
bool keep_files = false)
|
||||
: dir_(directory),
|
||||
prefix_(prefix),
|
||||
ext_(extension),
|
||||
keep_files_(keep_files),
|
||||
records_per_file_(records_per_file)
|
||||
{
|
||||
MFEM_VERIFY(records_per_file_ > 0, "SegmentedFileCheckpointStorage: records_per_file must be > 0.");
|
||||
MFEM_VERIFY(!dir_.empty(), "SegmentedFileCheckpointStorage: empty directory.");
|
||||
|
||||
if (create_dir)
|
||||
{
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
std::error_code ec;
|
||||
mfem_fs::create_directories(mfem_fs::path(dir_), ec);
|
||||
MFEM_VERIFY(!ec, "SegmentedFileCheckpointStorage: failed to create directory.");
|
||||
#else
|
||||
MFEM_ABORT("SegmentedFileCheckpointStorage: create_dir=true requires <filesystem> support.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Handle InvalidHandle() const { return (Handle)-1; }
|
||||
bool IsValid(const Handle &h) const { return h >= 0; }
|
||||
|
||||
Handle Store(Snapshot &&snap)
|
||||
{
|
||||
const Handle id = AllocateId_();
|
||||
EnsureMetaSize_(id);
|
||||
|
||||
const std::int64_t seg = SegmentId_(id);
|
||||
const std::string path = SegmentPath_(seg);
|
||||
|
||||
const std::uint64_t offset = AppendRecord_(path, snap);
|
||||
const std::uint64_t bytes = last_payload_bytes_;
|
||||
|
||||
meta_[id].valid = true;
|
||||
meta_[id].seg_id = seg;
|
||||
meta_[id].offset = offset;
|
||||
meta_[id].bytes = bytes;
|
||||
|
||||
touched_segments_.insert(seg);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void Read(const Handle &h, Func &&f) const
|
||||
{
|
||||
MFEM_VERIFY(IsValid(h), "SegmentedFileCheckpointStorage: Read invalid handle.");
|
||||
MFEM_VERIFY(h < (Handle)meta_.size(), "SegmentedFileCheckpointStorage: Read out-of-range handle.");
|
||||
MFEM_VERIFY(meta_[h].valid, "SegmentedFileCheckpointStorage: Read on erased handle.");
|
||||
|
||||
const std::string path = SegmentPath_(meta_[h].seg_id);
|
||||
|
||||
std::ifstream is(path, std::ios::binary);
|
||||
MFEM_VERIFY(is.is_open(), "SegmentedFileCheckpointStorage: open-for-read failed.");
|
||||
|
||||
// Seek to record start
|
||||
is.seekg((std::streamoff)meta_[h].offset, std::ios::beg);
|
||||
MFEM_VERIFY(is.good(), "SegmentedFileCheckpointStorage: seekg failed.");
|
||||
|
||||
std::uint64_t payload_bytes = 0;
|
||||
is.read(reinterpret_cast<char*>(&payload_bytes), sizeof(payload_bytes));
|
||||
MFEM_VERIFY(is.good(), "SegmentedFileCheckpointStorage: read header failed.");
|
||||
MFEM_VERIFY(payload_bytes == meta_[h].bytes, "SegmentedFileCheckpointStorage: payload size mismatch.");
|
||||
|
||||
Snapshot snap = SnapshotIO::Read(is);
|
||||
MFEM_VERIFY(is.good(), "SegmentedFileCheckpointStorage: payload read failed.");
|
||||
|
||||
f(snap);
|
||||
}
|
||||
|
||||
void Erase(Handle &h)
|
||||
{
|
||||
if (!IsValid(h)) { h = InvalidHandle(); return; }
|
||||
MFEM_VERIFY(h < (Handle)meta_.size(), "SegmentedFileCheckpointStorage: Erase out-of-range handle.");
|
||||
|
||||
meta_[h].valid = false;
|
||||
|
||||
if (!keep_files_)
|
||||
{
|
||||
free_.push_back(h); // allow handle reuse
|
||||
}
|
||||
|
||||
h = InvalidHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optional cleanup helper (NOT part of the required storage interface).
|
||||
*
|
||||
* Deletes all segment files touched by this storage object.
|
||||
*/
|
||||
void PurgeAllFiles()
|
||||
{
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
for (auto seg : touched_segments_)
|
||||
{
|
||||
const std::string path = SegmentPath_(seg);
|
||||
std::error_code ec;
|
||||
mfem_fs::remove(mfem_fs::path(path), ec);
|
||||
// Best-effort cleanup:
|
||||
MFEM_VERIFY(!ec, "SegmentedFileCheckpointStorage: failed to remove segment file.");
|
||||
}
|
||||
touched_segments_.clear();
|
||||
#else
|
||||
MFEM_ABORT("SegmentedFileCheckpointStorage: PurgeAllFiles requires <filesystem> support.");
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
struct Meta
|
||||
{
|
||||
bool valid = false;
|
||||
std::int64_t seg_id = 0;
|
||||
std::uint64_t offset = 0;
|
||||
std::uint64_t bytes = 0;
|
||||
};
|
||||
|
||||
std::string dir_, prefix_, ext_;
|
||||
bool keep_files_ = false;
|
||||
std::int64_t records_per_file_ = 4096;
|
||||
|
||||
mutable Handle next_id_ = 0;
|
||||
mutable std::vector<Handle> free_;
|
||||
std::vector<Meta> meta_;
|
||||
|
||||
mutable std::uint64_t last_payload_bytes_ = 0;
|
||||
|
||||
std::set<std::int64_t> touched_segments_;
|
||||
|
||||
void EnsureMetaSize_(Handle id)
|
||||
{
|
||||
if ((std::size_t)id >= meta_.size())
|
||||
{
|
||||
meta_.resize((std::size_t)id + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Handle AllocateId_()
|
||||
{
|
||||
if (!keep_files_ && !free_.empty())
|
||||
{
|
||||
const Handle id = free_.back();
|
||||
free_.pop_back();
|
||||
return id;
|
||||
}
|
||||
return next_id_++;
|
||||
}
|
||||
|
||||
std::int64_t SegmentId_(Handle id) const
|
||||
{
|
||||
return (std::int64_t)(id / records_per_file_);
|
||||
}
|
||||
|
||||
std::string SegmentPath_(std::int64_t seg) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << prefix_ << std::setw(8) << std::setfill('0') << seg << ext_;
|
||||
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
mfem_fs::path p = mfem_fs::path(dir_) / mfem_fs::path(oss.str());
|
||||
return p.string();
|
||||
#else
|
||||
return dir_ + "/" + oss.str();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void EnsureFileExists_(const std::string &path)
|
||||
{
|
||||
// Try open for read/write; if missing, create.
|
||||
std::fstream fs(path, std::ios::binary | std::ios::in | std::ios::out);
|
||||
if (!fs.is_open())
|
||||
{
|
||||
std::ofstream create(path, std::ios::binary | std::ios::out);
|
||||
MFEM_VERIFY(create.is_open(), "SegmentedFileCheckpointStorage: file create failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Append framed record to file. Returns offset of record start. Updates last_payload_bytes_.
|
||||
std::uint64_t AppendRecord_(const std::string &path, const Snapshot &snap)
|
||||
{
|
||||
EnsureFileExists_(path);
|
||||
|
||||
std::fstream fs(path, std::ios::binary | std::ios::in | std::ios::out);
|
||||
MFEM_VERIFY(fs.is_open(), "SegmentedFileCheckpointStorage: open-for-append failed.");
|
||||
|
||||
fs.seekp(0, std::ios::end);
|
||||
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: seekp(end) failed.");
|
||||
|
||||
const std::streamoff begin = (std::streamoff)fs.tellp();
|
||||
MFEM_VERIFY(begin >= 0, "SegmentedFileCheckpointStorage: tellp failed.");
|
||||
|
||||
// placeholder for payload bytes
|
||||
std::uint64_t payload_bytes = 0;
|
||||
fs.write(reinterpret_cast<const char*>(&payload_bytes), sizeof(payload_bytes));
|
||||
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: write header failed.");
|
||||
|
||||
// payload
|
||||
SnapshotIO::Write(fs, snap);
|
||||
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: write payload failed.");
|
||||
|
||||
const std::streamoff end = (std::streamoff)fs.tellp();
|
||||
MFEM_VERIFY(end >= begin, "SegmentedFileCheckpointStorage: tellp end failed.");
|
||||
|
||||
payload_bytes = (std::uint64_t)(end - begin - (std::streamoff)sizeof(std::uint64_t));
|
||||
last_payload_bytes_ = payload_bytes;
|
||||
|
||||
// backpatch payload size
|
||||
fs.seekp(begin, std::ios::beg);
|
||||
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: seekp(begin) failed.");
|
||||
fs.write(reinterpret_cast<const char*>(&payload_bytes), sizeof(payload_bytes));
|
||||
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: backpatch failed.");
|
||||
|
||||
return (std::uint64_t)begin;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_SEGMENT_CHECKPOINT_STORAGE_HPP
|
||||
|
||||
@@ -0,0 +1,701 @@
|
||||
#include "mfem.hpp"
|
||||
|
||||
#include "dynamic_checkpointing.hpp"
|
||||
#include "fixed_slot_checkpoint_storage.hpp"
|
||||
#include "file_checkpoint_storage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* The forward state consists of vector and several additional values.
|
||||
* The goal is to demontrate how different storages can be used together
|
||||
* with DynamicCheckpointing in order to avoid unnecessary memory
|
||||
* allocations, data copies, and deallocations.
|
||||
* *********************************************************************/
|
||||
struct State
|
||||
{
|
||||
mfem::real_t time = 0.0;
|
||||
mfem::real_t obj = 0.0;
|
||||
mfem::Vector v;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// Snapshot type used by storage:
|
||||
// a lightweight view (non-owning).
|
||||
// ---------------------------
|
||||
struct StateSnapshotView
|
||||
{
|
||||
mfem::real_t time = 0.0;
|
||||
mfem::real_t obj = 0.0;
|
||||
|
||||
// Points to n*sizeof(real_t) bytes.
|
||||
// - during Store(): points to State::v host data
|
||||
// - during Read(): points into storage's internal scratch buffer
|
||||
const unsigned char *v_bytes = nullptr;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// Packer for fixed-slot storage
|
||||
// Layout in slot bytes:
|
||||
// [ time | obj | v[0..n-1] ]
|
||||
// ---------------------------
|
||||
class StateSnapshotViewPacker
|
||||
{
|
||||
public:
|
||||
explicit StateSnapshotViewPacker(int n) : n_(n)
|
||||
{
|
||||
MFEM_VERIFY(n_ > 0, "StateSnapshotViewPacker: n must be > 0.");
|
||||
}
|
||||
|
||||
std::size_t SlotBytes() const
|
||||
{
|
||||
return (std::size_t)(2 + n_) * sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
void Pack(const StateSnapshotView &s, void *dst) const
|
||||
{
|
||||
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
|
||||
MFEM_VERIFY(s.v_bytes != nullptr, "Pack: v_bytes is null.");
|
||||
|
||||
unsigned char *b = static_cast<unsigned char*>(dst);
|
||||
|
||||
std::memcpy(b + 0*sizeof(mfem::real_t), &s.time, sizeof(mfem::real_t));
|
||||
std::memcpy(b + 1*sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
|
||||
std::memcpy(b + 2*sizeof(mfem::real_t),
|
||||
s.v_bytes,
|
||||
(std::size_t)n_ * sizeof(mfem::real_t));
|
||||
}
|
||||
|
||||
// IMPORTANT:
|
||||
// Unpack produces a view into the provided src buffer.
|
||||
// The resulting pointer is only valid as long as src remains unchanged.
|
||||
void Unpack(const void *src, StateSnapshotView &out) const
|
||||
{
|
||||
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
|
||||
|
||||
const unsigned char *b = static_cast<const unsigned char*>(src);
|
||||
|
||||
std::memcpy(&out.time, b + 0*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
std::memcpy(&out.obj, b + 1*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
out.v_bytes = b + 2*sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
int N() const { return n_; }
|
||||
|
||||
private:
|
||||
int n_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Provides a recipe for runing dynamic checkpointing with memory storage
|
||||
* alocated as one single big block.
|
||||
* **************************************************************************/
|
||||
void run_fixed_slot_memory_storage( mfem::real_t Tfinal /*Final time*/,
|
||||
mfem::real_t dtime /*time step*/,
|
||||
int s /* checkpoint budget (real checkpoints) */,
|
||||
int n /*State vector size*/,
|
||||
bool print=true)
|
||||
{
|
||||
StateSnapshotViewPacker packer(n);
|
||||
|
||||
using Storage = mfem::FixedSlotMemoryCheckpointStorage<StateSnapshotView,
|
||||
StateSnapshotViewPacker>;
|
||||
Storage storage(s, packer);
|
||||
|
||||
// Snapshot type is StateSnapshotView
|
||||
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshotView, Storage>;
|
||||
Checkpointing ckpt(s, storage);
|
||||
|
||||
// Returns view of the State and avoids data transfer
|
||||
auto make_snapshot = [&](const State &u) -> StateSnapshotView
|
||||
{
|
||||
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
|
||||
const mfem::real_t *vh = u.v.HostRead(); // host pointer
|
||||
|
||||
StateSnapshotView snap;
|
||||
snap.time = u.time;
|
||||
snap.obj = u.obj;
|
||||
snap.v_bytes = reinterpret_cast<const unsigned char*>(vh);
|
||||
return snap;
|
||||
};
|
||||
|
||||
//Transfers data from the snaphot view to the State u_out.
|
||||
auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
|
||||
{
|
||||
u_out.time = snap.time;
|
||||
u_out.obj = snap.obj;
|
||||
|
||||
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
|
||||
mfem::real_t *vh = u_out.v.HostWrite();
|
||||
|
||||
std::memcpy(vh,
|
||||
snap.v_bytes,
|
||||
(std::size_t)n * sizeof(mfem::real_t));
|
||||
};
|
||||
|
||||
using Step = Checkpointing::Step;
|
||||
|
||||
State u;
|
||||
u.v.SetSize(n); u.v=0.0;
|
||||
|
||||
auto primal_step = [&](State &u_inout, Step i)
|
||||
{
|
||||
// advance u_inout.v, u_inout.time, u_inout.obj
|
||||
if(print)
|
||||
std::cout<<"Forward step: "<<i<<" time="<<u_inout.time<<" obj="<<u_inout.obj<<std::endl;
|
||||
|
||||
u_inout.time=i*0.1;
|
||||
u_inout.obj=i*0.2;
|
||||
};
|
||||
|
||||
struct AdjointState { /* ... */ };
|
||||
AdjointState q;
|
||||
|
||||
auto adjoint_step = [&](AdjointState &q_ip1_inout, const State &u_i, Step i)
|
||||
{
|
||||
// use u_i.time/u_i.obj/u_i.v to update adjoint
|
||||
if(print)
|
||||
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
|
||||
|
||||
};
|
||||
|
||||
// Forward sweep (unknown number of steps)
|
||||
mfem::real_t t=0.0;
|
||||
Step i=0;
|
||||
while(t<Tfinal)
|
||||
{
|
||||
// Store snapshot of u_i when manager decides; then primal_step u_i -> u_{i+1}
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t+=dtime;
|
||||
++i;
|
||||
}
|
||||
|
||||
mfem::out << "Total number of steps m="<<i-1<<"\n";
|
||||
mfem::out << "\nBackward sweep.\n";
|
||||
|
||||
const Step m=i;
|
||||
// Backward sweep
|
||||
State u_work;
|
||||
u_work.v.SetSize(n); // allocate once
|
||||
|
||||
for (Step i = m - 1; i >= 0; --i)
|
||||
{
|
||||
ckpt.BackwardStep(i, q, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (i == 0) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Provides a recipe for runing dynamic checkpointing with file storage
|
||||
* alocated as one single big file.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* The dynamic algorithm conceptually maintains s + 1 checkpoints, but the
|
||||
* last one is a placeholder checkpoint that “stores no solution and takes
|
||||
* little memory”; only s are real stored solutions.
|
||||
* So FixedSlotFileCheckpointStorage needs only s slots.
|
||||
* File behavior for FixedSlotFileCheckpointStorage:
|
||||
- Writes a small header + s * slot_bytes payload region,
|
||||
- Erase(handle) just returns the slot to the free list
|
||||
(file contents remain, will be overwritten later),
|
||||
no filesystem metadata churn.
|
||||
|
||||
* Device memory: Runs on GPU -packing to a host file requires a host pointer.
|
||||
Using:
|
||||
u.v.HostRead() for packing,
|
||||
u_out.v.HostWrite() for restore,
|
||||
keeps memmory access correct.
|
||||
* Snapshot pointer lifetime: In the read path, StateSnapshotViewPacker::Unpack
|
||||
sets snap.v_bytes to point into the storage’s internal read buffer. That
|
||||
pointer is only valid until the storage performs another Read/Store
|
||||
(and in general, one should treat it as valid only during restore_snapshot).
|
||||
*****************************************************************************/
|
||||
|
||||
void run_fixed_slot_file_storage(mfem::real_t Tfinal /*Final time*/,
|
||||
mfem::real_t dtime /*time step*/,
|
||||
int s /* checkpoint budget (real checkpoints) */,
|
||||
int n /*State vector size*/,
|
||||
const std::string &file_path,
|
||||
bool print=true)
|
||||
{
|
||||
// s = number of REAL checkpoints (placeholder doesn't store a snapshot)
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(n > 0, "Need n > 0.");
|
||||
|
||||
StateSnapshotViewPacker packer(n);
|
||||
|
||||
// Fixed-slot FILE storage:
|
||||
// - single file (file_path)
|
||||
// - max_slots = s (real checkpoints)
|
||||
// - slot_bytes = packer.SlotBytes()
|
||||
// - truncate=true to start fresh
|
||||
// - flush_on_store=false for performance (set true if you want extra safety)
|
||||
using Storage = mfem::FixedSlotFileCheckpointStorage<StateSnapshotView, StateSnapshotViewPacker>;
|
||||
Storage storage(file_path,
|
||||
/*max_slots=*/s,
|
||||
/*packer=*/packer,
|
||||
/*truncate=*/true,
|
||||
/*flush_on_store=*/false);
|
||||
|
||||
// Dynamic checkpointing manager using this storage.
|
||||
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshotView, Storage>;
|
||||
Checkpointing ckpt(s, storage);
|
||||
|
||||
// ---------------------------
|
||||
// Callbacks
|
||||
// ---------------------------
|
||||
|
||||
// Convert State -> SnapshotView (no allocation).
|
||||
auto make_snapshot = [&](const State &u) -> StateSnapshotView
|
||||
{
|
||||
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
|
||||
|
||||
// Ensure host pointer valid even when MFEM is using device memory.
|
||||
const mfem::real_t *vh = u.v.HostRead();
|
||||
|
||||
StateSnapshotView snap;
|
||||
snap.time = u.time;
|
||||
snap.obj = u.obj;
|
||||
snap.v_bytes = reinterpret_cast<const unsigned char*>(vh);
|
||||
return snap;
|
||||
};
|
||||
|
||||
// Convert SnapshotView -> State (must COPY out of v_bytes).
|
||||
auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
|
||||
{
|
||||
u_out.time = snap.time;
|
||||
u_out.obj = snap.obj;
|
||||
|
||||
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
|
||||
|
||||
mfem::real_t *vh = u_out.v.HostWrite();
|
||||
std::memcpy(vh,
|
||||
snap.v_bytes,
|
||||
(std::size_t)n * sizeof(mfem::real_t));
|
||||
};
|
||||
|
||||
// Example primal/adjoint step signatures (replace with your own):
|
||||
using Step = Checkpointing::Step;
|
||||
|
||||
auto primal_step = [&](State &u_inout, Step i)
|
||||
{
|
||||
if(print)
|
||||
std::cout<<"Forward step: "<<i<<" time="<<u_inout.time<<" obj="<<u_inout.obj<<std::endl;
|
||||
|
||||
u_inout.time=i*0.1;
|
||||
u_inout.obj=i*0.2;
|
||||
// update u_inout.time, u_inout.obj, u_inout.v
|
||||
};
|
||||
|
||||
struct AdjointState
|
||||
{
|
||||
// your adjoint variables, e.g., mfem::Vector lambda;
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](AdjointState &q_ip1_inout, const State &u_i, Step i)
|
||||
{
|
||||
// update adjoint using u_i
|
||||
if(print)
|
||||
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// Forward/backward skeleton
|
||||
// ---------------------------
|
||||
State u;
|
||||
u.v.SetSize(n);
|
||||
u.time = 0.0;
|
||||
u.obj = 0.0;
|
||||
u.v = 0.0;
|
||||
|
||||
// Forward sweep (unknown number of steps)
|
||||
mfem::real_t t=0.0;
|
||||
Step i=0;
|
||||
while(t<Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t+=dtime;
|
||||
++i;
|
||||
}
|
||||
|
||||
mfem::out << "Total number of steps m="<<i-1<<"\n";
|
||||
mfem::out << "\nBackward sweep.\n";
|
||||
|
||||
// Backward sweep
|
||||
AdjointState q;
|
||||
const Step m=i;
|
||||
State u_work;
|
||||
u_work.v.SetSize(n);
|
||||
|
||||
for (Step i = m - 1; i >= 0; --i)
|
||||
{
|
||||
ckpt.BackwardStep(i, q, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (i == 0) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
The following classes implement the IO and the data necessery for dynamic
|
||||
checkpointing with mfem::FileCheckpointStorage (one file per stored snapshot).
|
||||
With FileCheckpointStorage, the Snapshot must own the data which will be
|
||||
written, i.e. it cannot be a “view” containing pointers into State::v
|
||||
as those pointers would be meaningless when read back.
|
||||
*******************************************************************************/
|
||||
|
||||
// The StateSnapshot is just a copy of the State
|
||||
using StateSnapshot=State;
|
||||
|
||||
struct StateSnapshotBinaryIO
|
||||
{
|
||||
static void Write(std::ostream &os, const StateSnapshot &s)
|
||||
{
|
||||
os.write(reinterpret_cast<const char*>(&s.time), sizeof(mfem::real_t));
|
||||
os.write(reinterpret_cast<const char*>(&s.obj), sizeof(mfem::real_t));
|
||||
MFEM_VERIFY(os.good(), "StateSnapshotBinaryIO: write time/obj failed.");
|
||||
|
||||
const std::int64_t n = (std::int64_t)s.v.Size();
|
||||
os.write(reinterpret_cast<const char*>(&n), sizeof(n));
|
||||
MFEM_VERIFY(os.good(), "StateSnapshotBinaryIO: write vector size failed.");
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
const mfem::real_t *vh = s.v.HostRead(); // ensure host pointer
|
||||
os.write(reinterpret_cast<const char*>(vh),
|
||||
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
|
||||
MFEM_VERIFY(os.good(), "StateSnapshotBinaryIO: write vector payload failed.");
|
||||
}
|
||||
}
|
||||
|
||||
static StateSnapshot Read(std::istream &is)
|
||||
{
|
||||
StateSnapshot s;
|
||||
|
||||
is.read(reinterpret_cast<char*>(&s.time), sizeof(mfem::real_t));
|
||||
is.read(reinterpret_cast<char*>(&s.obj), sizeof(mfem::real_t));
|
||||
MFEM_VERIFY(is.good(), "StateSnapshotBinaryIO: read time/obj failed.");
|
||||
|
||||
std::int64_t n = 0;
|
||||
is.read(reinterpret_cast<char*>(&n), sizeof(n));
|
||||
MFEM_VERIFY(is.good(), "StateSnapshotBinaryIO: read vector size failed.");
|
||||
MFEM_VERIFY(n >= 0, "StateSnapshotBinaryIO: invalid negative vector size.");
|
||||
|
||||
s.v.SetSize((int)n);
|
||||
if (n > 0)
|
||||
{
|
||||
mfem::real_t *vh = s.v.HostWrite(); // ensure host pointer
|
||||
is.read(reinterpret_cast<char*>(vh),
|
||||
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
|
||||
MFEM_VERIFY(is.good(), "StateSnapshotBinaryIO: read vector payload failed.");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
Important differences vs FixedSlotFileCheckpointStorage
|
||||
Metadata overhead
|
||||
|
||||
FileCheckpointStorage typically:
|
||||
*creates a new file for each Store() (plus a temp file rename),
|
||||
*deletes a file on each Erase() (unless keep_files=true).
|
||||
|
||||
Dynamic checkpointing can perform many store/erase operations during
|
||||
forward and during recomputation in reverse, so this can hammer filesystem
|
||||
metadata on parallel filesystems.
|
||||
|
||||
If that’s a concern, prefer:
|
||||
*fixed-slot single-file storage, or
|
||||
*segmented “range file” storage (few files + offsets).
|
||||
|
||||
Correctness note about pointers:
|
||||
This is why we used an owning snapshot (StateSnapshot): storing a “view”
|
||||
with v_bytes into file storage would store pointer values, which are
|
||||
meaningless when read back.
|
||||
*****************************************************************************/
|
||||
|
||||
void run_file_storage(mfem::real_t Tfinal /*Final time*/,
|
||||
mfem::real_t dtime /*time step*/,
|
||||
int s /* checkpoint budget (real checkpoints) */,
|
||||
int n /*State vector size*/,
|
||||
const std::string &directory,
|
||||
bool print=true)
|
||||
{
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(n > 0, "Need n > 0.");
|
||||
|
||||
// One file per snapshot (create_dir=true). keep_files=false means Erase() removes files.
|
||||
using Storage = mfem::FileCheckpointStorage<StateSnapshot, StateSnapshotBinaryIO>;
|
||||
Storage storage(directory, "ckpt_", ".bin", /*create_dir=*/true, /*keep_files=*/false);
|
||||
|
||||
// Snapshot type = StateSnapshot (owning)
|
||||
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshot, Storage>;
|
||||
Checkpointing ckpt(s, storage);
|
||||
|
||||
using Step = Checkpointing::Step;
|
||||
|
||||
// ---- Callbacks ----
|
||||
|
||||
// Make an owning snapshot from the current State (deep copy of vector payload)
|
||||
auto make_snapshot = [&](const State &u) -> StateSnapshot
|
||||
{
|
||||
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
|
||||
|
||||
StateSnapshot snap;
|
||||
snap.time = u.time;
|
||||
snap.obj = u.obj;
|
||||
|
||||
snap.v.SetSize(n);
|
||||
const mfem::real_t *src = u.v.HostRead();
|
||||
mfem::real_t *dst = snap.v.HostWrite();
|
||||
std::memcpy(dst, src, (std::size_t)n * sizeof(mfem::real_t));
|
||||
|
||||
return snap; // move into storage
|
||||
};
|
||||
|
||||
// Restore from snapshot into an actual State (copy payload)
|
||||
auto restore_snapshot = [&](const StateSnapshot &snap, State &u_out)
|
||||
{
|
||||
u_out.time = snap.time;
|
||||
u_out.obj = snap.obj;
|
||||
|
||||
MFEM_VERIFY(snap.v.Size() == n, "restore_snapshot: snapshot vector size mismatch.");
|
||||
|
||||
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
|
||||
|
||||
const mfem::real_t *src = snap.v.HostRead();
|
||||
mfem::real_t *dst = u_out.v.HostWrite();
|
||||
std::memcpy(dst, src, (std::size_t)n * sizeof(mfem::real_t));
|
||||
};
|
||||
|
||||
// Example primal step signature (replace with your integrator)
|
||||
auto primal_step = [&](State &u_inout, Step i)
|
||||
{
|
||||
// advance u_inout.time, u_inout.obj, u_inout.v
|
||||
if(print)
|
||||
std::cout<<"Forward step: "<<i<<" time="<<u_inout.time<<" obj="<<u_inout.obj<<std::endl;
|
||||
|
||||
u_inout.time=i*0.1;
|
||||
u_inout.obj=i*0.2;
|
||||
};
|
||||
|
||||
// Example adjoint state and adjoint step signature (replace with yours)
|
||||
struct AdjointState
|
||||
{
|
||||
// e.g., mfem::Vector lambda;
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](AdjointState &q_ip1_inout, const State &u_i, Step i)
|
||||
{
|
||||
// update adjoint using u_i
|
||||
if(print)
|
||||
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
|
||||
};
|
||||
|
||||
// ---- Forward/backward skeleton ----
|
||||
|
||||
State u;
|
||||
u.v.SetSize(n);
|
||||
u.time = 0.0;
|
||||
u.obj = 0.0;
|
||||
u.v = 0.0;
|
||||
|
||||
|
||||
|
||||
// Forward sweep (unknown number of steps)
|
||||
mfem::real_t t=0.0;
|
||||
Step i=0;
|
||||
while(t<Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t+=dtime;
|
||||
++i;
|
||||
}
|
||||
|
||||
mfem::out << "Total number of steps m="<<i-1<<"\n";
|
||||
mfem::out << "\nBackward sweep.\n";
|
||||
|
||||
// backward
|
||||
const Step m=i;
|
||||
AdjointState q;
|
||||
State u_work;
|
||||
u_work.v.SetSize(n);
|
||||
|
||||
for (Step i = m - 1; i >= 0; --i)
|
||||
{
|
||||
ckpt.BackwardStep(i, q, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (i == 0) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Using standard in memory storage. Works with variable snaphot sizes.
|
||||
* The size of the vector (n) is provided in order to compare the run to
|
||||
* the other implementations.
|
||||
***************************************************************************/
|
||||
|
||||
void run_in_memory_storage(mfem::real_t Tfinal /*Final time*/,
|
||||
mfem::real_t dtime /*time step*/,
|
||||
int s /* checkpoint budget (real checkpoints) */,
|
||||
int n /*State vector size*/,
|
||||
bool print=true)
|
||||
{
|
||||
InMemoryCheckpointStorage<StateSnapshot> storage;
|
||||
|
||||
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshot,
|
||||
InMemoryCheckpointStorage<StateSnapshot>>;
|
||||
|
||||
Checkpointing ckpt(s, storage);
|
||||
|
||||
auto make_snapshot = [&](const State &u) -> StateSnapshot
|
||||
{
|
||||
StateSnapshot snap;
|
||||
snap.time = u.time;
|
||||
snap.obj = u.obj;
|
||||
snap.v = u.v; // deep copy (MFEM handles memory)
|
||||
return snap; // moved into storage by Store()
|
||||
};
|
||||
|
||||
auto restore_snapshot = [&](const StateSnapshot &snap, State &u_out)
|
||||
{
|
||||
u_out.time = snap.time;
|
||||
u_out.obj = snap.obj;
|
||||
u_out.v = snap.v; // deep copy back into working state
|
||||
};
|
||||
|
||||
using Step = Checkpointing::Step;
|
||||
|
||||
// Primal step: u_i -> u_{i+1}
|
||||
auto primal_step = [&](State &u, Step i)
|
||||
{
|
||||
// ... update u.time, u.obj, u.v ...
|
||||
if(print)
|
||||
std::cout<<"Forward step: "<<i<<" time="<<u.time<<" obj="<<u.obj<<std::endl;
|
||||
|
||||
u.time=i*0.1;
|
||||
u.obj=i*0.2;
|
||||
};
|
||||
|
||||
// Adjoint step: q_{i+1} -> q_i using u_i
|
||||
struct AdjointState
|
||||
{
|
||||
// e.g. mfem::Vector lambda;
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](AdjointState &q, const State &u_i, Step i)
|
||||
{
|
||||
// ... update q using u_i ...
|
||||
if(print)
|
||||
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
|
||||
};
|
||||
|
||||
State u;
|
||||
u.v.SetSize(n);
|
||||
u.v=0.0;
|
||||
u.time = 0.0;
|
||||
u.obj = 0.0;
|
||||
|
||||
// Forward sweep (unknown number of steps)
|
||||
mfem::real_t t=0.0;
|
||||
Step i=0;
|
||||
while(t<Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t+=dtime;
|
||||
++i;
|
||||
}
|
||||
|
||||
mfem::out << "Total number of steps m="<<i-1<<"\n";
|
||||
mfem::out << "\nBackward sweep.\n";
|
||||
|
||||
// Backward
|
||||
const Step m=i;
|
||||
AdjointState q;
|
||||
State u_work; // used to restore/recompute primal state at step i
|
||||
u_work.v.SetSize(u.v.Size());
|
||||
|
||||
for (Step i = m - 1; i >= 0; --i)
|
||||
{
|
||||
ckpt.BackwardStep(i, q, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (i == 0) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int s = 5/* checkpoint budget (real checkpoints) */;
|
||||
int n = 30 /* fixed State.v size */;
|
||||
std::string filepath="dynamic_ckpts.bin"; /*file name for fixed slot single file*/
|
||||
std::string directory="dyn_ckpts";
|
||||
|
||||
mfem::real_t Tfinal=1;
|
||||
mfem::real_t dt=0.1;
|
||||
int print=true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&s, "-s",
|
||||
"--checkpoints", "Checkpoint budget s (real checkpoints).");
|
||||
args.AddOption(&n, "-n",
|
||||
"--size", "Size of the state vector.");
|
||||
args.AddOption(&Tfinal, "-T",
|
||||
"--tfinal", "Terminate when accumulated time reaches Tfinal.");
|
||||
args.AddOption(&dt, "-dt", "--dt", "Time step.");
|
||||
args.AddOption(&filepath, "-file", "--file",
|
||||
"File name for storing the checkpoints.");
|
||||
args.AddOption(&directory, "-dir", "--dir",
|
||||
"Directory for storing the checkpoints.");
|
||||
args.AddOption(&print, "-print", "--print",
|
||||
"Turn on/off printing." );
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(mfem::out);
|
||||
|
||||
double run_time;
|
||||
|
||||
mfem::out<<"\nFixedSlotMemoryStorage Start\n";
|
||||
mfem::tic();
|
||||
run_fixed_slot_memory_storage(Tfinal, dt, s, n, print);
|
||||
run_time=mfem::toc();
|
||||
mfem::out<<"FixedSlotMemoryStorage time= "<<run_time<<std::endl;
|
||||
|
||||
mfem::out<<"\nFixedSlotFileStorage Start\n";
|
||||
mfem::tic();
|
||||
run_fixed_slot_file_storage(Tfinal,dt, s,n, filepath,print);
|
||||
run_time=mfem::toc();
|
||||
mfem::out<<"FixedSlotFileStorage time= "<<run_time<<std::endl;
|
||||
|
||||
mfem::out<<"\nFileStorage Start\n";
|
||||
mfem::tic();
|
||||
run_file_storage(Tfinal,dt, s,n, directory, print);
|
||||
run_time=mfem::toc();
|
||||
mfem::out<<"FileStorage time= "<<run_time<<std::endl;
|
||||
|
||||
mfem::out<<"\nInMemoryStorage\n";
|
||||
mfem::tic();
|
||||
run_in_memory_storage(Tfinal,dt, s,n, print);
|
||||
run_time=mfem::toc();
|
||||
mfem::out<<"InMemoryStorage time= "<<run_time<<std::endl;
|
||||
|
||||
mfem::out << "\nDone.\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
#include "mfem.hpp"
|
||||
#include "dynamic_checkpointing.hpp"
|
||||
#include "file_checkpoint_storage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
static double LogisticStep(double u, double alpha, double dt)
|
||||
{
|
||||
return u + dt * alpha * u * (1.0 - u);
|
||||
}
|
||||
|
||||
static double LogisticJac(double u, double alpha, double dt)
|
||||
{
|
||||
// d/du [u + dt*alpha*u*(1-u)] = 1 + dt*alpha*(1 - 2u)
|
||||
return 1.0 + dt * alpha * (1.0 - 2.0*u);
|
||||
}
|
||||
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
static void PurgeDir(const std::string &dir)
|
||||
{
|
||||
std::error_code ec;
|
||||
mfem_fs::remove_all(mfem_fs::path(dir), ec);
|
||||
mfem_fs::create_directories(mfem_fs::path(dir), ec);
|
||||
MFEM_VERIFY(!ec, "Failed to purge/create directory.");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// ---------- shared options ----------
|
||||
int s = 3;
|
||||
double dt0 = 0.02;
|
||||
double omega = 0.2;
|
||||
double Tfinal = 1.0;
|
||||
bool keep_files = false;
|
||||
bool purge_dirs = true;
|
||||
|
||||
// scalar problem params
|
||||
double alpha = 2.0;
|
||||
double u0 = 0.2;
|
||||
double target_s = 0.7;
|
||||
double eps = 1e-7;
|
||||
|
||||
// vector problem params
|
||||
int n = 16;
|
||||
double target_v = 0.7;
|
||||
|
||||
std::string dir_scalar = "chk_scalar_files";
|
||||
std::string dir_vector = "chk_vector_files";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
|
||||
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&omega, "-om", "--omega", "Omega for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&Tfinal, "-T", "--tfinal", "Stop when accumulated time reaches Tfinal.");
|
||||
args.AddOption(&keep_files, "-k", "--keep-files", "-nk", "--no-keep-files",
|
||||
"Keep checkpoint files (debug).");
|
||||
args.AddOption(&purge_dirs, "-p", "--purge-dirs", "-np", "--no-purge-dirs",
|
||||
"Purge checkpoint directories at start (recommended).");
|
||||
|
||||
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
|
||||
args.AddOption(&u0, "-u0", "--u0", "Scalar initial u0.");
|
||||
args.AddOption(&target_s, "-ts", "--target-scalar", "Scalar target.");
|
||||
args.AddOption(&eps, "-eps", "--fd-eps", "FD epsilon (scalar and vector directional).");
|
||||
|
||||
args.AddOption(&n, "-n", "--size", "Vector dimension.");
|
||||
args.AddOption(&target_v, "-tv", "--target-vector", "Vector target value per component.");
|
||||
|
||||
args.AddOption(&dir_scalar, "-ds", "--dir-scalar", "Directory for scalar checkpoint files.");
|
||||
args.AddOption(&dir_vector, "-dv", "--dir-vector", "Directory for vector checkpoint files.");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(mfem::out);
|
||||
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
|
||||
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
|
||||
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
if (purge_dirs)
|
||||
{
|
||||
PurgeDir(dir_scalar);
|
||||
PurgeDir(dir_vector);
|
||||
}
|
||||
#else
|
||||
MFEM_VERIFY(!purge_dirs, "purge_dirs requires <filesystem> support.");
|
||||
#endif
|
||||
|
||||
using StepS = mfem::DynamicCheckpointing<double, mfem::FileCheckpointStorage<double>>::Step;
|
||||
auto dt_func = [&](StepS i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
mfem::out << std::setprecision(15);
|
||||
|
||||
// ============================================================
|
||||
// A) Scalar run (double) with FileCheckpointStorage
|
||||
// ============================================================
|
||||
{
|
||||
mfem::FileCheckpointStorage<double> storage(dir_scalar, "ckpt_", ".bin", true, keep_files);
|
||||
mfem::DynamicCheckpointing<double, mfem::FileCheckpointStorage<double>> ckpt(s, storage);
|
||||
|
||||
auto make_snapshot = [](const double &u) { return u; };
|
||||
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](double &u, StepS i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
u = LogisticStep(u, alpha, dt);
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](double &lambda, const double &u_i, StepS i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
const double dF = LogisticJac(u_i, alpha, dt);
|
||||
lambda *= dF;
|
||||
};
|
||||
|
||||
double u = u0;
|
||||
double t_phys = 0.0;
|
||||
StepS i = 0;
|
||||
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func(i);
|
||||
++i;
|
||||
}
|
||||
const StepS m = i;
|
||||
const double u_m = u;
|
||||
const double J = 0.5 * (u_m - target_s) * (u_m - target_s);
|
||||
|
||||
double lambda = (u_m - target_s); // terminal adjoint
|
||||
double u_work = 0.0;
|
||||
|
||||
for (StepS j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
|
||||
const double dJ_du0_adj = lambda;
|
||||
|
||||
// FD check
|
||||
auto forward_only_J = [&](double u_init)
|
||||
{
|
||||
double uu = u_init;
|
||||
double tt = 0.0;
|
||||
StepS k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
uu = LogisticStep(uu, alpha, dt_func(k));
|
||||
tt += dt_func(k);
|
||||
++k;
|
||||
}
|
||||
const double r = uu - target_s;
|
||||
return 0.5 * r * r;
|
||||
};
|
||||
|
||||
const double Jp = forward_only_J(u0 + eps);
|
||||
const double Jm = forward_only_J(u0 - eps);
|
||||
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
|
||||
|
||||
mfem::out << "\n[FileIO][Scalar]\n";
|
||||
mfem::out << " m steps = " << m << "\n";
|
||||
mfem::out << " u_m = " << u_m << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " dJ/du0 adjoint = " << dJ_du0_adj << "\n";
|
||||
mfem::out << " dJ/du0 FD = " << dJ_du0_fd << "\n";
|
||||
mfem::out << " abs err = " << std::abs(dJ_du0_adj - dJ_du0_fd) << "\n";
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// B) Vector run (mfem::Vector) with FileCheckpointStorage
|
||||
// ============================================================
|
||||
{
|
||||
mfem::FileCheckpointStorage<mfem::Vector> storage(dir_vector, "ckpt_", ".bin", true, keep_files);
|
||||
mfem::DynamicCheckpointing<mfem::Vector, mfem::FileCheckpointStorage<mfem::Vector>> ckpt(s, storage);
|
||||
|
||||
using StepV = mfem::DynamicCheckpointing<mfem::Vector, mfem::FileCheckpointStorage<mfem::Vector>>::Step;
|
||||
|
||||
auto dt_func_v = [&](StepV i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
auto make_snapshot = [](const mfem::Vector &u) { return u; };
|
||||
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](mfem::Vector &u, StepV i)
|
||||
{
|
||||
const double dt = dt_func_v(i);
|
||||
for (int j = 0; j < u.Size(); ++j)
|
||||
{
|
||||
u[j] = LogisticStep(u[j], alpha, dt);
|
||||
}
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, StepV i)
|
||||
{
|
||||
const double dt = dt_func_v(i);
|
||||
MFEM_ASSERT(lambda.Size() == u_i.Size(), "Size mismatch.");
|
||||
for (int j = 0; j < lambda.Size(); ++j)
|
||||
{
|
||||
lambda[j] *= LogisticJac(u_i[j], alpha, dt);
|
||||
}
|
||||
};
|
||||
|
||||
mfem::Vector u0v(n), ut(n);
|
||||
for (int j = 0; j < n; ++j) { u0v[j] = 0.2 + 0.05 * std::cos(0.7*(j+1)); }
|
||||
ut = target_v;
|
||||
|
||||
mfem::Vector u = u0v;
|
||||
double t_phys = 0.0;
|
||||
StepV i = 0;
|
||||
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func_v(i);
|
||||
++i;
|
||||
}
|
||||
const StepV m = i;
|
||||
const mfem::Vector u_m = u;
|
||||
|
||||
mfem::Vector diff(u_m);
|
||||
diff -= ut;
|
||||
const double J = 0.5 * mfem::InnerProduct(diff, diff);
|
||||
|
||||
// backward
|
||||
mfem::Vector lambda = diff; // terminal adjoint
|
||||
mfem::Vector u_work(n);
|
||||
|
||||
for (StepV j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
const mfem::Vector &grad_u0 = lambda;
|
||||
|
||||
// directional FD check
|
||||
mfem::Vector v(n);
|
||||
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3*(j+1)) + 0.1; }
|
||||
|
||||
auto forward_only_J = [&](const mfem::Vector &u_init)
|
||||
{
|
||||
mfem::Vector uu = u_init;
|
||||
double tt = 0.0;
|
||||
StepV k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
primal_step(uu, k);
|
||||
tt += dt_func_v(k);
|
||||
++k;
|
||||
}
|
||||
mfem::Vector dd(uu);
|
||||
dd -= ut;
|
||||
return 0.5 * mfem::InnerProduct(dd, dd);
|
||||
};
|
||||
|
||||
mfem::Vector u_plus(u0v), u_minus(u0v);
|
||||
u_plus.Add(eps, v);
|
||||
u_minus.Add(-eps, v);
|
||||
|
||||
const double Jp = forward_only_J(u_plus);
|
||||
const double Jm = forward_only_J(u_minus);
|
||||
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
|
||||
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
|
||||
|
||||
mfem::out << "\n[FileIO][Vector]\n";
|
||||
mfem::out << " m steps = " << m << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
|
||||
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
|
||||
mfem::out << " abs err = " << std::abs(dJ_dir_adj - dJ_dir_fd) << "\n";
|
||||
mfem::out << " ||u_m||_2 = " << u_m.Norml2() << "\n";
|
||||
mfem::out << " ||u_m - u_target||2 = " << diff.Norml2() << "\n";
|
||||
}
|
||||
|
||||
mfem::out << "\nDone.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
#include "mfem.hpp"
|
||||
|
||||
#include "dynamic_checkpointing.hpp"
|
||||
#include "fixed_slot_checkpoint_storage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
// Logistic ODE (scalar or componentwise):
|
||||
// du/dt = alpha*u*(1-u)
|
||||
// Explicit Euler:
|
||||
// u_{i+1} = u_i + dt_i * alpha*u_i*(1-u_i)
|
||||
//
|
||||
// Objective:
|
||||
// Scalar: J = 0.5*(u_m - target)^2
|
||||
// Vector: J = 0.5*||u_m - u_target||^2
|
||||
//
|
||||
// Discrete adjoint:
|
||||
// dF/du = 1 + dt_i*alpha*(1 - 2*u_i)
|
||||
// lambda_i = (dF/du at u_i) * lambda_{i+1}
|
||||
|
||||
static inline double LogisticStep(const double u, const double alpha, const double dt)
|
||||
{
|
||||
return u + dt * alpha * u * (1.0 - u);
|
||||
}
|
||||
|
||||
static inline double LogisticJac(const double u, const double alpha, const double dt)
|
||||
{
|
||||
return 1.0 + dt * alpha * (1.0 - 2.0*u);
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// Scalar run (double)
|
||||
// --------------------------
|
||||
template <typename Storage>
|
||||
static void RunScalarFixedSlot(int s,
|
||||
Storage &storage,
|
||||
double alpha,
|
||||
double dt0,
|
||||
double omega,
|
||||
double Tfinal,
|
||||
double u0,
|
||||
double target,
|
||||
double eps)
|
||||
{
|
||||
using CKPT = mfem::DynamicCheckpointing<double, Storage>;
|
||||
using Step = typename CKPT::Step;
|
||||
|
||||
CKPT ckpt(s, storage);
|
||||
|
||||
auto dt_func = [&](Step i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
auto make_snapshot = [](const double &u) { return u; };
|
||||
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](double &u, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
u = LogisticStep(u, alpha, dt);
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](double &lambda, const double &u_i, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
lambda *= LogisticJac(u_i, alpha, dt);
|
||||
};
|
||||
|
||||
// Forward sweep (unknown m; stop on accumulated physical time)
|
||||
double u = u0;
|
||||
double t_phys = 0.0;
|
||||
Step i = 0;
|
||||
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func(i);
|
||||
++i;
|
||||
}
|
||||
|
||||
const Step m = i;
|
||||
const double u_m = u;
|
||||
const double J = 0.5 * (u_m - target) * (u_m - target);
|
||||
|
||||
// Backward sweep
|
||||
MFEM_VERIFY(m > 0, "Scalar run produced m=0 steps.");
|
||||
|
||||
double lambda = (u_m - target); // terminal condition dJ/du_m
|
||||
double u_work = 0.0;
|
||||
|
||||
for (Step j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
const double dJ_du0_adj = lambda;
|
||||
|
||||
// FD check
|
||||
auto forward_only_J = [&](double u_init)
|
||||
{
|
||||
double uu = u_init;
|
||||
double tt = 0.0;
|
||||
Step k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
uu = LogisticStep(uu, alpha, dt_func(k));
|
||||
tt += dt_func(k);
|
||||
++k;
|
||||
}
|
||||
const double r = uu - target;
|
||||
return 0.5 * r * r;
|
||||
};
|
||||
|
||||
const double Jp = forward_only_J(u0 + eps);
|
||||
const double Jm = forward_only_J(u0 - eps);
|
||||
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
|
||||
|
||||
mfem::out << "\n[FixedSlot][Scalar]\n";
|
||||
mfem::out << " m steps = " << m << "\n";
|
||||
mfem::out << " t_phys = " << t_phys << "\n";
|
||||
mfem::out << " u_m = " << u_m << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " dJ/du0 adjoint = " << dJ_du0_adj << "\n";
|
||||
mfem::out << " dJ/du0 FD = " << dJ_du0_fd << "\n";
|
||||
mfem::out << " abs err = " << std::abs(dJ_du0_adj - dJ_du0_fd) << "\n";
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// Vector run (mfem::Vector)
|
||||
// --------------------------
|
||||
template <typename Storage>
|
||||
static void RunVectorFixedSlot(int s,
|
||||
Storage &storage,
|
||||
int n,
|
||||
double alpha,
|
||||
double dt0,
|
||||
double omega,
|
||||
double Tfinal,
|
||||
double target_val,
|
||||
double eps)
|
||||
{
|
||||
using CKPT = mfem::DynamicCheckpointing<mfem::Vector, Storage>;
|
||||
using Step = typename CKPT::Step;
|
||||
|
||||
CKPT ckpt(s, storage);
|
||||
|
||||
auto dt_func = [&](Step i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
auto make_snapshot = [](const mfem::Vector &u) { return u; };
|
||||
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](mfem::Vector &u, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
for (int j = 0; j < u.Size(); ++j)
|
||||
{
|
||||
u[j] = LogisticStep(u[j], alpha, dt);
|
||||
}
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, Step i)
|
||||
{
|
||||
|
||||
const double dt = dt_func(i);
|
||||
MFEM_ASSERT(lambda.Size() == u_i.Size(), "lambda and u_i size mismatch.");
|
||||
for (int j = 0; j < lambda.Size(); ++j)
|
||||
{
|
||||
lambda[j] *= LogisticJac(u_i[j], alpha, dt);
|
||||
}
|
||||
};
|
||||
|
||||
// Initial condition and target
|
||||
mfem::Vector u0(n), u_target(n);
|
||||
for (int j = 0; j < n; ++j)
|
||||
{
|
||||
u0[j] = 0.2 + 0.05 * std::cos(0.7 * (j + 1));
|
||||
}
|
||||
u_target = target_val;
|
||||
|
||||
// Forward sweep
|
||||
mfem::Vector u = u0;
|
||||
double t_phys = 0.0;
|
||||
Step i = 0;
|
||||
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func(i);
|
||||
++i;
|
||||
}
|
||||
|
||||
const Step m = i;
|
||||
const mfem::Vector u_m = u;
|
||||
|
||||
mfem::Vector diff(u_m);
|
||||
diff -= u_target;
|
||||
const double J = 0.5 * mfem::InnerProduct(diff, diff);
|
||||
|
||||
// Backward sweep
|
||||
MFEM_VERIFY(m > 0, "Vector run produced m=0 steps.");
|
||||
|
||||
mfem::Vector lambda = diff; // terminal adjoint = dJ/du_m
|
||||
mfem::Vector u_work(n);
|
||||
|
||||
for (Step j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
const mfem::Vector &grad_u0 = lambda;
|
||||
|
||||
// Directional FD check
|
||||
mfem::Vector v(n);
|
||||
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3 * (j + 1)) + 0.1; }
|
||||
|
||||
auto forward_only_J = [&](const mfem::Vector &u_init)
|
||||
{
|
||||
mfem::Vector uu = u_init;
|
||||
double tt = 0.0;
|
||||
Step k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
primal_step(uu, k);
|
||||
tt += dt_func(k);
|
||||
++k;
|
||||
}
|
||||
mfem::Vector dd(uu);
|
||||
dd -= u_target;
|
||||
return 0.5 * mfem::InnerProduct(dd, dd);
|
||||
};
|
||||
|
||||
mfem::Vector u_plus(u0), u_minus(u0);
|
||||
u_plus.Add(eps, v);
|
||||
u_minus.Add(-eps, v);
|
||||
|
||||
const double Jp = forward_only_J(u_plus);
|
||||
const double Jm = forward_only_J(u_minus);
|
||||
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
|
||||
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
|
||||
|
||||
mfem::out << "\n[FixedSlot][Vector]\n";
|
||||
mfem::out << " n = " << n << "\n";
|
||||
mfem::out << " m steps = " << m << "\n";
|
||||
mfem::out << " t_phys = " << t_phys << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " ||u_m||_2 = " << u_m.Norml2() << "\n";
|
||||
mfem::out << " ||u_m-u_target|| = " << diff.Norml2() << "\n";
|
||||
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
|
||||
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
|
||||
mfem::out << " abs err = " << std::abs(dJ_dir_adj - dJ_dir_fd) << "\n";
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Backend selection:
|
||||
// 0 = fixed-slot memory (single RAM block)
|
||||
// 1 = fixed-slot file (single file with fixed offsets)
|
||||
int backend = 0;
|
||||
|
||||
// Common parameters
|
||||
int s = 8; // number of REAL stored checkpoints
|
||||
double alpha = 2.0;
|
||||
double dt0 = 0.02;
|
||||
double omega = 0.2;
|
||||
double Tfinal = 1.0;
|
||||
double eps = 1e-7;
|
||||
|
||||
// Scalar parameters
|
||||
double u0 = 0.2;
|
||||
double target_s = 0.7;
|
||||
|
||||
// Vector parameters
|
||||
int n = 64; // must stay fixed for fixed-slot vector packing
|
||||
double target_v = 0.7;
|
||||
|
||||
// File backend parameters
|
||||
std::string scalar_file = "scalar_fixedslots.bin";
|
||||
std::string vector_file = "vector_fixedslots.bin";
|
||||
bool truncate_files = true;
|
||||
bool flush_on_store = true; // safer for demo correctness with iostreams
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&backend, "-b", "--backend",
|
||||
"Backend: 0=memory fixed-slots, 1=file fixed-slots (single file).");
|
||||
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
|
||||
|
||||
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
|
||||
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&omega, "-om", "--omega", "Omega for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&Tfinal, "-T", "--tfinal", "Stop when accumulated time reaches Tfinal.");
|
||||
args.AddOption(&eps, "-eps", "--fd-eps", "FD epsilon.");
|
||||
|
||||
args.AddOption(&u0, "-u0", "--u0", "Scalar initial condition u0.");
|
||||
args.AddOption(&target_s, "-ts", "--target-scalar", "Scalar target.");
|
||||
|
||||
args.AddOption(&n, "-n", "--size", "Vector dimension (fixed).");
|
||||
args.AddOption(&target_v, "-tv", "--target-vector", "Vector target value per component.");
|
||||
|
||||
args.AddOption(&scalar_file, "-sf", "--scalar-file", "File for scalar fixed-slot storage.");
|
||||
args.AddOption(&vector_file, "-vf", "--vector-file", "File for vector fixed-slot storage.");
|
||||
args.AddOption(&truncate_files, "-tr", "--truncate", "-ntr", "--no-truncate",
|
||||
"Truncate checkpoint files on startup.");
|
||||
args.AddOption(&flush_on_store, "-fl", "--flush", "-nfl", "--no-flush",
|
||||
"Flush on each Store() (demo-safety; slower).");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(mfem::out);
|
||||
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(n > 0, "Need n > 0.");
|
||||
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
|
||||
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
|
||||
|
||||
mfem::out << std::setprecision(15);
|
||||
|
||||
if (backend == 0)
|
||||
{
|
||||
mfem::out << "\nUsing fixed-slot MEMORY backend (single RAM block)\n";
|
||||
|
||||
// Scalar: fixed-size POD => trivial packer
|
||||
mfem::FixedSlotMemoryCheckpointStorage<double> stor_s(s);
|
||||
RunScalarFixedSlot(s, stor_s, alpha, dt0, omega, Tfinal, u0, target_s, eps);
|
||||
|
||||
// Vector: fixed-size packing (n must remain constant)
|
||||
mfem::FixedVectorPacker packer(n);
|
||||
mfem::FixedSlotMemoryCheckpointStorage<mfem::Vector, mfem::FixedVectorPacker> stor_v(s, packer);
|
||||
RunVectorFixedSlot(s, stor_v, n, alpha, dt0, omega, Tfinal, target_v, eps);
|
||||
}
|
||||
else if (backend == 1)
|
||||
{
|
||||
mfem::out << "\nUsing fixed-slot FILE backend (single file with fixed offsets)\n";
|
||||
|
||||
// Scalar file
|
||||
mfem::FixedSlotFileCheckpointStorage<double> stor_s(scalar_file, s,
|
||||
mfem::TrivialFixedPacker<double>(),
|
||||
truncate_files,
|
||||
flush_on_store);
|
||||
RunScalarFixedSlot(s, stor_s, alpha, dt0, omega, Tfinal, u0, target_s, eps);
|
||||
|
||||
// Vector file (fixed-size packing with n)
|
||||
mfem::FixedVectorPacker packer(n);
|
||||
mfem::FixedSlotFileCheckpointStorage<mfem::Vector, mfem::FixedVectorPacker>
|
||||
stor_v(vector_file, s, packer, truncate_files, flush_on_store);
|
||||
RunVectorFixedSlot(s, stor_v, n, alpha, dt0, omega, Tfinal, target_v, eps);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unknown backend. Use -b 0 (memory) or -b 1 (file).");
|
||||
}
|
||||
|
||||
mfem::out << "\nDone.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "mfem.hpp"
|
||||
|
||||
#include "revolve_checkpointing.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
// the forward state consists of vector and
|
||||
// several additional values
|
||||
struct State
|
||||
{
|
||||
mfem::real_t time = 0.0;
|
||||
mfem::real_t obj = 0.0;
|
||||
mfem::Vector v;
|
||||
};
|
||||
|
||||
/// class to manage the Snaphot Layout
|
||||
class StateSnapshotLayout
|
||||
{
|
||||
public:
|
||||
// Constructor: n - size of the vector in the state
|
||||
explicit StateSnapshotLayout(int n) : n_(n)
|
||||
{
|
||||
MFEM_VERIFY(n_ > 0, "StateSnapshotLayout: n must be > 0.");
|
||||
}
|
||||
|
||||
// returns the total size of the snapshot in mfem::real_t
|
||||
int N() const { return n_; }
|
||||
|
||||
// returns the total number of bytes necessary to store the state
|
||||
int Bytes() const
|
||||
{
|
||||
return (2 + n_) * (int)sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
// Pack State -> bytes (host bytes)
|
||||
void Pack(const State &s, unsigned char *dst, int bytes) const
|
||||
{
|
||||
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
|
||||
MFEM_VERIFY(bytes == Bytes(), "Pack: snapshot byte size mismatch.");
|
||||
MFEM_VERIFY(s.v.Size() == n_, "Pack: State.v size mismatch.");
|
||||
|
||||
// Ensure we read from host even if MFEM uses device memory.
|
||||
const mfem::real_t *vh = s.v.HostRead();
|
||||
|
||||
std::memcpy(dst + 0*sizeof(mfem::real_t), &s.time, sizeof(mfem::real_t));
|
||||
std::memcpy(dst + 1*sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
|
||||
std::memcpy(dst + 2*sizeof(mfem::real_t),
|
||||
vh, (std::size_t)n_ * sizeof(mfem::real_t));
|
||||
}
|
||||
|
||||
// Unpack bytes -> State (host write)
|
||||
void Unpack(const unsigned char *src, int bytes, State &out) const
|
||||
{
|
||||
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
|
||||
MFEM_VERIFY(bytes == Bytes(), "Unpack: snapshot byte size mismatch.");
|
||||
|
||||
if (out.v.Size() != n_) { out.v.SetSize(n_); }
|
||||
mfem::real_t *vh = out.v.HostWrite();
|
||||
|
||||
std::memcpy(&out.time, src + 0*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
std::memcpy(&out.obj, src + 1*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
std::memcpy(vh,
|
||||
src + 2*sizeof(mfem::real_t),
|
||||
(std::size_t)n_ * sizeof(mfem::real_t));
|
||||
}
|
||||
|
||||
private:
|
||||
int n_ = 0;
|
||||
};
|
||||
|
||||
/* The REVOLVE manager (as provided earlier) uses this callback style:
|
||||
|
||||
* make_snapshot(const State&, uint8_t* out, size_t bytes)
|
||||
* restore_snapshot(State&, const uint8_t* in, size_t bytes)
|
||||
* primal_step(int step, State&)
|
||||
* adjoint_step(int step, const State&, AdjointState&)
|
||||
|
||||
and a storage backend with:
|
||||
* Save(slot, bytes)
|
||||
* Load(slot, bytes)
|
||||
|
||||
REVOLVE manager will:
|
||||
*request storing snapshots into checkpoint slots (takeshot)
|
||||
*request restoring a checkpoint slot (restore)
|
||||
*request recomputation forward (advance)
|
||||
*then request the next adjoint step (firsturn / youturn)
|
||||
all while using only (Ncheck) stored checkpoints.
|
||||
|
||||
Fixed-step REVOLVE manager:
|
||||
* Snapshot is raw bytes packed/unpacked by your callbacks.
|
||||
* Storage is a fixed indexed array (Save(slot) / Load(slot)),
|
||||
because REVOLVE addresses checkpoints by slot index.
|
||||
|
||||
Best use when Nsteps is known in the begining of the simulation.
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Backend selection:
|
||||
// 0 = fixed-slot memory (single RAM block)
|
||||
// 1 = fixed-slot file (single file with fixed offsets)
|
||||
int backend = 0;
|
||||
|
||||
const int n = 100/* fixed State.v size */;
|
||||
StateSnapshotLayout layout(n);
|
||||
const std::size_t snapshot_bytes = (std::size_t)layout.Bytes();
|
||||
|
||||
const int Nsteps = 20 /* known number of time steps */;
|
||||
const int Ncheck = 5 /* number of checkpoints (snaps) */;
|
||||
|
||||
// Memory backend (single block)
|
||||
// FixedSlotMemoryStorage storage(Ncheck, snapshot_bytes);
|
||||
// FixedStepRevolveCheckpointing<FixedSlotMemoryStorage>
|
||||
// cktp(Nsteps, Ncheck, snapshot_bytes, storage);
|
||||
|
||||
// or file backend (single file)
|
||||
FixedSlotFileStorage storage("revolve_ckpts.bin", Ncheck, snapshot_bytes);
|
||||
FixedStepRevolveCheckpointing<FixedSlotFileStorage>
|
||||
cktp(Nsteps, Ncheck, snapshot_bytes, storage);
|
||||
|
||||
|
||||
auto make_snapshot = [&](const State &s, uint8_t *outb, std::size_t bytes)
|
||||
{
|
||||
MFEM_VERIFY(bytes == snapshot_bytes, "make_snapshot: byte size mismatch");
|
||||
layout.Pack(s, reinterpret_cast<unsigned char*>(outb), (int)bytes);
|
||||
};
|
||||
|
||||
auto restore_snapshot = [&](State &s, const uint8_t *inb, std::size_t bytes)
|
||||
{
|
||||
MFEM_VERIFY(bytes == snapshot_bytes, "restore_snapshot: byte size mismatch");
|
||||
layout.Unpack(reinterpret_cast<const unsigned char*>(inb), (int)bytes, s);
|
||||
};
|
||||
|
||||
// set the work state
|
||||
State u_work;
|
||||
u_work.v.SetSize(n); u_work.v=0.0;
|
||||
|
||||
State u;
|
||||
u.v.SetSize(n); u.v=0.0; u.obj=0.0; u.time=0.0;
|
||||
// init u.time, u.obj, u.v ...
|
||||
|
||||
auto primal_step = [&](int step, State &u_inout)
|
||||
{
|
||||
(void)step;
|
||||
// advance u_inout -> u_{step+1}
|
||||
};
|
||||
|
||||
struct AdjointState { /* ... */ };
|
||||
AdjointState lambda;
|
||||
|
||||
auto adjoint_step = [&](int step, const State &u_step, AdjointState &lambda_inout)
|
||||
{
|
||||
(void)step;
|
||||
// update lambda_{step+1} -> lambda_step using u_step
|
||||
};
|
||||
|
||||
// Forward: i = 0..Nsteps-1
|
||||
for (int i = 0; i < Nsteps; ++i)
|
||||
{
|
||||
cktp.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
}
|
||||
|
||||
// Reverse: i = Nsteps-1..0
|
||||
for (int i = Nsteps - 1; i >= 0; --i)
|
||||
{
|
||||
cktp.BackwardStep(i, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
}
|
||||
|
||||
mfem::out << "\nDone.\n";
|
||||
return 0;
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
#include "mfem.hpp"
|
||||
#include "dynamic_checkpointing.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
// Scalar logistic ODE:
|
||||
// du/dt = alpha*u*(1-u)
|
||||
// Explicit Euler with variable dt(i):
|
||||
// u_{i+1} = u_i + dt_i*alpha*u_i*(1-u_i)
|
||||
// Objective:
|
||||
// J = 0.5*(u_m - target)^2
|
||||
// Discrete adjoint (scalar):
|
||||
// lambda_m = (u_m - target)
|
||||
// lambda_i = (dF/du at u_i)^T * lambda_{i+1}
|
||||
// dF/du = 1 + dt_i*alpha*(1 - 2*u_i)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int s = 3;
|
||||
double alpha = 2.0;
|
||||
double dt0 = 0.02;
|
||||
double omega = 0.2;
|
||||
double Tfinal = 1.0;
|
||||
double u0 = 0.2;
|
||||
double target = 0.7;
|
||||
double eps = 1e-7;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
|
||||
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
|
||||
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&omega, "-om", "--omega", "Omega in dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&Tfinal, "-T", "--tfinal", "Terminate when accumulated time reaches Tfinal.");
|
||||
args.AddOption(&u0, "-u0", "--u0", "Initial scalar state u0.");
|
||||
args.AddOption(&target, "-ut", "--target", "Target value in J=0.5*(u_m-target)^2.");
|
||||
args.AddOption(&eps, "-eps", "--fd-eps", "Finite-difference epsilon.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(mfem::out);
|
||||
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
|
||||
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
|
||||
|
||||
using Step = mfem::DynamicCheckpointing<double>::Step;
|
||||
|
||||
auto dt_func = [&](Step i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
// Checkpoint manager:
|
||||
// State = double
|
||||
// Snapshot= double
|
||||
mfem::DynamicCheckpointing<double> ckpt(s);
|
||||
|
||||
auto make_snapshot = [](const double &u) -> double { return u; };
|
||||
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](double &u, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
u = u + dt * alpha * u * (1.0 - u);
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](double &lambda, const double &u_i, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
const double dF_du = 1.0 + dt * alpha * (1.0 - 2.0 * u_i);
|
||||
lambda = dF_du * lambda;
|
||||
};
|
||||
|
||||
// ---------------- Forward sweep (unknown m) ----------------
|
||||
double u = u0;
|
||||
double t_phys = 0.0;
|
||||
|
||||
Step i = 0;
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func(i);
|
||||
++i;
|
||||
}
|
||||
|
||||
const Step m = i;
|
||||
const double u_m = u;
|
||||
const double J = 0.5 * (u_m - target) * (u_m - target);
|
||||
|
||||
mfem::out << std::setprecision(15);
|
||||
mfem::out << "\n[Scalar] Forward finished:\n";
|
||||
mfem::out << " m (steps) = " << m << "\n";
|
||||
mfem::out << " t_phys = " << t_phys << "\n";
|
||||
mfem::out << " u_m = " << u_m << "\n";
|
||||
mfem::out << " J = " << J << "\n\n";
|
||||
|
||||
mfem::out << "[Scalar] Checkpoint set after forward sweep (step, level, stored):\n";
|
||||
for (const auto &cp : ckpt.GetCheckpointInfo())
|
||||
{
|
||||
mfem::out << " step=" << cp.step
|
||||
<< ", level=" << cp.level
|
||||
<< ", stored=" << (cp.stored ? "yes" : "no")
|
||||
<< (cp.stored ? "" : " (placeholder)")
|
||||
<< "\n";
|
||||
}
|
||||
mfem::out << "\n";
|
||||
|
||||
MFEM_VERIFY(m > 0, "Forward produced m=0 steps; nothing to do.");
|
||||
|
||||
// ---------------- Backward sweep (adjoint) ----------------
|
||||
double lambda = (u_m - target); // terminal adjoint = dJ/du_m
|
||||
double u_work = 0.0; // scratch primal state u_i
|
||||
|
||||
for (Step j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; } // avoid signed underflow
|
||||
}
|
||||
|
||||
const double dJ_du0_adjoint = lambda;
|
||||
|
||||
// ---------------- Finite-difference gradient check ----------------
|
||||
auto forward_only_J = [&](double u_init)
|
||||
{
|
||||
double uu = u_init;
|
||||
double tt = 0.0;
|
||||
Step k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
primal_step(uu, k);
|
||||
tt += dt_func(k);
|
||||
++k;
|
||||
}
|
||||
const double r = (uu - target);
|
||||
return 0.5 * r * r;
|
||||
};
|
||||
|
||||
const double Jp = forward_only_J(u0 + eps);
|
||||
const double Jm = forward_only_J(u0 - eps);
|
||||
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
|
||||
|
||||
const double abs_err = std::abs(dJ_du0_adjoint - dJ_du0_fd);
|
||||
const double rel_err = abs_err / (std::abs(dJ_du0_fd) + 1e-30);
|
||||
|
||||
mfem::out << "[Scalar] Gradient check (dJ/du0):\n";
|
||||
mfem::out << " adjoint = " << dJ_du0_adjoint << "\n";
|
||||
mfem::out << " FD = " << dJ_du0_fd << "\n";
|
||||
mfem::out << " abs err = " << abs_err << "\n";
|
||||
mfem::out << " rel err = " << rel_err << "\n\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
#include "mfem.hpp"
|
||||
#include "dynamic_checkpointing.hpp"
|
||||
#include "segment_checkpoint_storage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
static double LogisticStep(double u, double alpha, double dt)
|
||||
{
|
||||
return u + dt * alpha * u * (1.0 - u);
|
||||
}
|
||||
|
||||
static double LogisticJac(double u, double alpha, double dt)
|
||||
{
|
||||
return 1.0 + dt * alpha * (1.0 - 2.0*u);
|
||||
}
|
||||
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
static void PurgeDir(const std::string &dir)
|
||||
{
|
||||
std::error_code ec;
|
||||
mfem_fs::remove_all(mfem_fs::path(dir), ec);
|
||||
mfem_fs::create_directories(mfem_fs::path(dir), ec);
|
||||
MFEM_VERIFY(!ec, "Failed to purge/create directory.");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int s = 3;
|
||||
double dt0 = 0.02;
|
||||
double omega = 0.2;
|
||||
double Tfinal = 1.0;
|
||||
|
||||
// scalar params
|
||||
double alpha = 2.0;
|
||||
double u0 = 0.2;
|
||||
double target_s = 0.7;
|
||||
double eps = 1e-7;
|
||||
|
||||
// vector params
|
||||
int n = 16;
|
||||
double target_v = 0.7;
|
||||
|
||||
// segmented storage params
|
||||
std::string dir_scalar = "chk_scalar_segments";
|
||||
std::string dir_vector = "chk_vector_segments";
|
||||
long long records_per_file = 4096;
|
||||
bool keep_files = false;
|
||||
bool purge_dirs = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
|
||||
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&omega, "-om", "--omega", "Omega for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&Tfinal, "-T", "--tfinal", "Stop when accumulated time reaches Tfinal.");
|
||||
|
||||
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
|
||||
args.AddOption(&u0, "-u0", "--u0", "Scalar initial u0.");
|
||||
args.AddOption(&target_s, "-ts", "--target-scalar", "Scalar target.");
|
||||
args.AddOption(&eps, "-eps", "--fd-eps", "FD epsilon (scalar and vector directional).");
|
||||
|
||||
args.AddOption(&n, "-n", "--size", "Vector dimension.");
|
||||
args.AddOption(&target_v, "-tv", "--target-vector", "Vector target value per component.");
|
||||
|
||||
args.AddOption(&dir_scalar, "-ds", "--dir-scalar", "Directory for scalar segment files.");
|
||||
args.AddOption(&dir_vector, "-dv", "--dir-vector", "Directory for vector segment files.");
|
||||
args.AddOption(&records_per_file, "-rpf", "--records-per-file",
|
||||
"How many handles belong to one segment file (range size).");
|
||||
|
||||
args.AddOption(&keep_files, "-k", "--keep-files", "-nk", "--no-keep-files",
|
||||
"Keep segment files (debug).");
|
||||
args.AddOption(&purge_dirs, "-p", "--purge-dirs", "-np", "--no-purge-dirs",
|
||||
"Purge checkpoint directories at start (recommended).");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(mfem::out);
|
||||
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(records_per_file > 0, "Need records_per_file > 0.");
|
||||
|
||||
#if MFEM_HAVE_FILESYSTEM
|
||||
if (purge_dirs)
|
||||
{
|
||||
PurgeDir(dir_scalar);
|
||||
PurgeDir(dir_vector);
|
||||
}
|
||||
#else
|
||||
MFEM_VERIFY(!purge_dirs, "purge_dirs requires <filesystem> support.");
|
||||
#endif
|
||||
|
||||
using StepS = mfem::DynamicCheckpointing<
|
||||
double,
|
||||
mfem::SegmentedFileCheckpointStorage<double>>::Step;
|
||||
|
||||
auto dt_func = [&](StepS i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
mfem::out << std::setprecision(15);
|
||||
|
||||
// ============================================================
|
||||
// A) Scalar with segmented storage
|
||||
// ============================================================
|
||||
mfem::SegmentedFileCheckpointStorage<double> stor_s(dir_scalar, records_per_file,
|
||||
"seg_", ".bin", true, keep_files);
|
||||
{
|
||||
mfem::DynamicCheckpointing<double, mfem::SegmentedFileCheckpointStorage<double>> ckpt(s, stor_s);
|
||||
|
||||
auto make_snapshot = [](const double &u) { return u; };
|
||||
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](double &u, StepS i)
|
||||
{
|
||||
u = LogisticStep(u, alpha, dt_func(i));
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](double &lambda, const double &u_i, StepS i)
|
||||
{
|
||||
lambda *= LogisticJac(u_i, alpha, dt_func(i));
|
||||
};
|
||||
|
||||
double u = u0;
|
||||
double t_phys = 0.0;
|
||||
StepS i = 0;
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func(i);
|
||||
++i;
|
||||
}
|
||||
const StepS m = i;
|
||||
const double u_m = u;
|
||||
const double J = 0.5 * (u_m - target_s) * (u_m - target_s);
|
||||
|
||||
double lambda = (u_m - target_s);
|
||||
double u_work = 0.0;
|
||||
|
||||
for (StepS j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
const double dJ_du0_adj = lambda;
|
||||
|
||||
auto forward_only_J = [&](double u_init)
|
||||
{
|
||||
double uu = u_init;
|
||||
double tt = 0.0;
|
||||
StepS k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
uu = LogisticStep(uu, alpha, dt_func(k));
|
||||
tt += dt_func(k);
|
||||
++k;
|
||||
}
|
||||
const double r = uu - target_s;
|
||||
return 0.5 * r * r;
|
||||
};
|
||||
|
||||
const double Jp = forward_only_J(u0 + eps);
|
||||
const double Jm = forward_only_J(u0 - eps);
|
||||
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
|
||||
|
||||
mfem::out << "\n[SegmentIO][Scalar]\n";
|
||||
mfem::out << " m steps = " << m << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " dJ/du0 adjoint = " << dJ_du0_adj << "\n";
|
||||
mfem::out << " dJ/du0 FD = " << dJ_du0_fd << "\n";
|
||||
mfem::out << " abs err = " << std::abs(dJ_du0_adj - dJ_du0_fd) << "\n";
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// B) Vector with segmented storage
|
||||
// ============================================================
|
||||
mfem::SegmentedFileCheckpointStorage<mfem::Vector> stor_v(dir_vector, records_per_file,
|
||||
"seg_", ".bin", true, keep_files);
|
||||
{
|
||||
mfem::DynamicCheckpointing<mfem::Vector, mfem::SegmentedFileCheckpointStorage<mfem::Vector>>
|
||||
ckpt(s, stor_v);
|
||||
|
||||
using StepV = mfem::DynamicCheckpointing<
|
||||
mfem::Vector,
|
||||
mfem::SegmentedFileCheckpointStorage<mfem::Vector>>::Step;
|
||||
|
||||
auto dt_func_v = [&](StepV i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
auto make_snapshot = [](const mfem::Vector &u) { return u; };
|
||||
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](mfem::Vector &u, StepV i)
|
||||
{
|
||||
const double dt = dt_func_v(i);
|
||||
for (int j = 0; j < u.Size(); ++j)
|
||||
{
|
||||
u[j] = LogisticStep(u[j], alpha, dt);
|
||||
}
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, StepV i)
|
||||
{
|
||||
const double dt = dt_func_v(i);
|
||||
MFEM_ASSERT(lambda.Size() == u_i.Size(), "Size mismatch.");
|
||||
for (int j = 0; j < lambda.Size(); ++j)
|
||||
{
|
||||
lambda[j] *= LogisticJac(u_i[j], alpha, dt);
|
||||
}
|
||||
};
|
||||
|
||||
mfem::Vector u0v(n), ut(n);
|
||||
for (int j = 0; j < n; ++j) { u0v[j] = 0.2 + 0.05 * std::cos(0.7*(j+1)); }
|
||||
ut = target_v;
|
||||
|
||||
mfem::Vector u = u0v;
|
||||
double t_phys = 0.0;
|
||||
StepV i = 0;
|
||||
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func_v(i);
|
||||
++i;
|
||||
}
|
||||
const StepV m = i;
|
||||
|
||||
mfem::Vector diff(u);
|
||||
diff -= ut;
|
||||
const double J = 0.5 * mfem::InnerProduct(diff, diff);
|
||||
|
||||
mfem::Vector lambda = diff;
|
||||
mfem::Vector u_work(n);
|
||||
|
||||
for (StepV j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
const mfem::Vector &grad_u0 = lambda;
|
||||
|
||||
mfem::Vector v(n);
|
||||
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3*(j+1)) + 0.1; }
|
||||
|
||||
auto forward_only_J = [&](const mfem::Vector &u_init)
|
||||
{
|
||||
mfem::Vector uu = u_init;
|
||||
double tt = 0.0;
|
||||
StepV k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
primal_step(uu, k);
|
||||
tt += dt_func_v(k);
|
||||
++k;
|
||||
}
|
||||
mfem::Vector dd(uu);
|
||||
dd -= ut;
|
||||
return 0.5 * mfem::InnerProduct(dd, dd);
|
||||
};
|
||||
|
||||
mfem::Vector u_plus(u0v), u_minus(u0v);
|
||||
u_plus.Add(eps, v);
|
||||
u_minus.Add(-eps, v);
|
||||
|
||||
const double Jp = forward_only_J(u_plus);
|
||||
const double Jm = forward_only_J(u_minus);
|
||||
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
|
||||
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
|
||||
|
||||
mfem::out << "\n[SegmentIO][Vector]\n";
|
||||
mfem::out << " m steps = " << m << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
|
||||
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
|
||||
mfem::out << " abs err = " << std::abs(dJ_dir_adj - dJ_dir_fd) << "\n";
|
||||
mfem::out << " ||u - u_target||2 = " << diff.Norml2() << "\n";
|
||||
}
|
||||
|
||||
// Optional cleanup: delete segment files this run touched
|
||||
if (!keep_files)
|
||||
{
|
||||
stor_s.PurgeAllFiles();
|
||||
stor_v.PurgeAllFiles();
|
||||
}
|
||||
|
||||
mfem::out << "\nDone.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "mfem.hpp"
|
||||
#include "dynamic_checkpointing.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
// Vector logistic ODE (componentwise):
|
||||
// du/dt = alpha*u*(1-u) (applied to each component)
|
||||
// Explicit Euler:
|
||||
// u_{i+1}[j] = u_i[j] + dt_i*alpha*u_i[j]*(1-u_i[j])
|
||||
// Objective:
|
||||
// J = 0.5*||u_m - u_target||^2
|
||||
// Terminal adjoint:
|
||||
// lambda_m = u_m - u_target
|
||||
// Adjoint step (componentwise):
|
||||
// lambda_i[j] = (1 + dt_i*alpha*(1 - 2*u_i[j])) * lambda_{i+1}[j]
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int s = 3;
|
||||
int n = 16;
|
||||
double alpha = 2.0;
|
||||
double dt0 = 0.02;
|
||||
double omega = 0.2;
|
||||
double Tfinal = 1.0;
|
||||
double target_val = 0.7;
|
||||
double eps = 1e-7;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
|
||||
args.AddOption(&n, "-n", "--size", "Vector dimension n.");
|
||||
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
|
||||
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&omega, "-om", "--omega", "Omega in dt(i)=dt0*(1+0.5*sin(omega*i)).");
|
||||
args.AddOption(&Tfinal, "-T", "--tfinal", "Terminate when accumulated time reaches Tfinal.");
|
||||
args.AddOption(&target_val,"-tv", "--target", "Target value for each component.");
|
||||
args.AddOption(&eps, "-eps", "--fd-eps", "Finite-difference epsilon (directional).");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(mfem::out);
|
||||
|
||||
MFEM_VERIFY(s > 0, "Need s > 0.");
|
||||
MFEM_VERIFY(n > 0, "Need n > 0.");
|
||||
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
|
||||
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
|
||||
|
||||
using Step = mfem::DynamicCheckpointing<mfem::Vector>::Step;
|
||||
|
||||
auto dt_func = [&](Step i)
|
||||
{
|
||||
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
|
||||
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
|
||||
return dt;
|
||||
};
|
||||
|
||||
// Checkpoint manager:
|
||||
// State = mfem::Vector
|
||||
// Snapshot= mfem::Vector
|
||||
mfem::DynamicCheckpointing<mfem::Vector> ckpt(s);
|
||||
|
||||
auto make_snapshot = [](const mfem::Vector &u) -> mfem::Vector { return u; };
|
||||
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
|
||||
|
||||
auto primal_step = [&](mfem::Vector &u, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
for (int j = 0; j < u.Size(); ++j)
|
||||
{
|
||||
const double uj = u[j];
|
||||
u[j] = uj + dt * alpha * uj * (1.0 - uj);
|
||||
}
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, Step i)
|
||||
{
|
||||
const double dt = dt_func(i);
|
||||
MFEM_ASSERT(lambda.Size() == u_i.Size(), "lambda and u_i size mismatch");
|
||||
for (int j = 0; j < lambda.Size(); ++j)
|
||||
{
|
||||
const double dF_du = 1.0 + dt * alpha * (1.0 - 2.0 * u_i[j]);
|
||||
lambda[j] *= dF_du;
|
||||
}
|
||||
};
|
||||
|
||||
// Initial condition and target
|
||||
mfem::Vector u0(n), u_target(n);
|
||||
for (int j = 0; j < n; ++j)
|
||||
{
|
||||
u0[j] = 0.2 + 0.05 * std::cos(0.7 * (j + 1));
|
||||
}
|
||||
u_target = target_val;
|
||||
|
||||
// ---------------- Forward sweep (unknown m) ----------------
|
||||
mfem::Vector u = u0;
|
||||
double t_phys = 0.0;
|
||||
|
||||
Step i = 0;
|
||||
while (t_phys < Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t_phys += dt_func(i);
|
||||
++i;
|
||||
}
|
||||
|
||||
const Step m = i;
|
||||
const mfem::Vector u_m = u;
|
||||
|
||||
mfem::Vector diff(u_m);
|
||||
diff -= u_target;
|
||||
const double J = 0.5 * mfem::InnerProduct(diff, diff);
|
||||
|
||||
mfem::out << std::setprecision(15);
|
||||
mfem::out << "\n[Vector] Forward finished:\n";
|
||||
mfem::out << " m (steps) = " << m << "\n";
|
||||
mfem::out << " t_phys = " << t_phys << "\n";
|
||||
mfem::out << " J = " << J << "\n";
|
||||
mfem::out << " ||u_m|| = " << u_m.Norml2() << "\n";
|
||||
mfem::out << " ||u_m-ut||= " << diff.Norml2() << "\n\n";
|
||||
|
||||
mfem::out << "[Vector] Checkpoint set after forward sweep (step, level, stored):\n";
|
||||
for (const auto &cp : ckpt.GetCheckpointInfo())
|
||||
{
|
||||
mfem::out << " step=" << cp.step
|
||||
<< ", level=" << cp.level
|
||||
<< ", stored=" << (cp.stored ? "yes" : "no")
|
||||
<< (cp.stored ? "" : " (placeholder)")
|
||||
<< "\n";
|
||||
}
|
||||
mfem::out << "\n";
|
||||
|
||||
MFEM_VERIFY(m > 0, "Forward produced m=0 steps; nothing to do.");
|
||||
|
||||
// ---------------- Backward sweep (adjoint) ----------------
|
||||
mfem::Vector lambda = diff; // terminal = dJ/du_m
|
||||
mfem::Vector u_work(n); // scratch primal u_i
|
||||
|
||||
for (Step j = m - 1; j >= 0; --j)
|
||||
{
|
||||
ckpt.BackwardStep(j, lambda, u_work,
|
||||
primal_step, adjoint_step,
|
||||
make_snapshot, restore_snapshot);
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
const mfem::Vector &grad_u0 = lambda;
|
||||
|
||||
// ---------------- Directional FD check ----------------
|
||||
mfem::Vector v(n);
|
||||
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3 * (j + 1)) + 0.1; }
|
||||
|
||||
auto forward_only_J = [&](const mfem::Vector &u_init)
|
||||
{
|
||||
mfem::Vector uu = u_init;
|
||||
double tt = 0.0;
|
||||
Step k = 0;
|
||||
while (tt < Tfinal)
|
||||
{
|
||||
primal_step(uu, k);
|
||||
tt += dt_func(k);
|
||||
++k;
|
||||
}
|
||||
mfem::Vector dd(uu);
|
||||
dd -= u_target;
|
||||
return 0.5 * mfem::InnerProduct(dd, dd);
|
||||
};
|
||||
|
||||
mfem::Vector u_plus(u0), u_minus(u0);
|
||||
u_plus.Add(eps, v);
|
||||
u_minus.Add(-eps, v);
|
||||
|
||||
const double Jp = forward_only_J(u_plus);
|
||||
const double Jm = forward_only_J(u_minus);
|
||||
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
|
||||
|
||||
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
|
||||
|
||||
const double abs_err = std::abs(dJ_dir_adj - dJ_dir_fd);
|
||||
const double rel_err = abs_err / (std::abs(dJ_dir_fd) + 1e-30);
|
||||
|
||||
mfem::out << "[Vector] Directional derivative check:\n";
|
||||
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
|
||||
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
|
||||
mfem::out << " abs err = " << abs_err << "\n";
|
||||
mfem::out << " rel err = " << rel_err << "\n\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# 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 EX_SOURCES linear_elasticity.hpp)
|
||||
list(APPEND EX_HEADERS linear_elasticity.cpp)
|
||||
|
||||
convert_filenames_to_full_paths(EX_SOURCES)
|
||||
convert_filenames_to_full_paths(EX_HEADERS)
|
||||
|
||||
set(EX_COMMON_FILES
|
||||
EXTRA_SOURCES ${EX_SOURCES}
|
||||
EXTRA_HEADERS ${EX_HEADERS})
|
||||
|
||||
add_mfem_miniapp(mtop_test_forward_le
|
||||
MAIN test_forward_le.cpp
|
||||
${EX_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(mtop_test_adjoint_le
|
||||
MAIN test_adjoint_le.cpp
|
||||
${EX_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
|
||||
endif (MFEM_USE_MPI)
|
||||
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
$MeshFormat
|
||||
2.2 0 8
|
||||
$EndMeshFormat
|
||||
$Nodes
|
||||
267
|
||||
1 -5 -5 0
|
||||
2 5 -5 0
|
||||
3 5 0 0
|
||||
4 3 0 0
|
||||
5 2.5 0 0
|
||||
6 0.25 0 0
|
||||
7 -0.25 0 0
|
||||
8 -2.5 0 0
|
||||
9 -3 0 0
|
||||
10 -5 0 0
|
||||
11 -4.5 -5 0
|
||||
12 -4 -5 0
|
||||
13 -3.5 -5 0
|
||||
14 -3 -5 0
|
||||
15 -2.5 -5 0
|
||||
16 -2 -5 0
|
||||
17 -1.5 -5 0
|
||||
18 -1 -5 0
|
||||
19 -0.5 -5 0
|
||||
20 0 -5 0
|
||||
21 0.5 -5 0
|
||||
22 1 -5 0
|
||||
23 1.5 -5 0
|
||||
24 2 -5 0
|
||||
25 2.5 -5 0
|
||||
26 3 -5 0
|
||||
27 3.5 -5 0
|
||||
28 4 -5 0
|
||||
29 4.5 -5 0
|
||||
30 5 -4.5 0
|
||||
31 5 -4 0
|
||||
32 5 -3.5 0
|
||||
33 5 -3 0
|
||||
34 5 -2.5 0
|
||||
35 5 -2 0
|
||||
36 5 -1.5 0
|
||||
37 5 -1 0
|
||||
38 5 -0.5 0
|
||||
39 4.5 0 0
|
||||
40 4 0 0
|
||||
41 3.5 0 0
|
||||
42 2.75 0 0
|
||||
43 2.125 0 0
|
||||
44 1.75 0 0
|
||||
45 1.375 0 0
|
||||
46 1 0 0
|
||||
47 0.625 0 0
|
||||
48 0 0 0
|
||||
49 -0.625 0 0
|
||||
50 -1 0 0
|
||||
51 -1.375 0 0
|
||||
52 -1.75 0 0
|
||||
53 -2.125 0 0
|
||||
54 -2.75 0 0
|
||||
55 -3.5 0 0
|
||||
56 -4 0 0
|
||||
57 -4.5 0 0
|
||||
58 -5 -0.5 0
|
||||
59 -5 -1 0
|
||||
60 -5 -1.5 0
|
||||
61 -5 -2 0
|
||||
62 -5 -2.5 0
|
||||
63 -5 -3 0
|
||||
64 -5 -3.5 0
|
||||
65 -5 -4 0
|
||||
66 -5 -4.5 0
|
||||
67 3.147692481109132e-08 -2.865638549497702 0
|
||||
68 0.7411835869316199 -1.439094140802561 0
|
||||
69 -0.7411835350444509 -1.43909394139115 0
|
||||
70 4.165134723562585e-08 -1.07528086308038 0
|
||||
71 0.6217064619398043 -3.063797051940143 0
|
||||
72 1.152592324474185 -3.183434480740315 0
|
||||
73 1.65143516768565 -3.26705431245132 0
|
||||
74 2.136365371184163 -3.328666155776893 0
|
||||
75 2.615028312537451 -3.375240988133206 0
|
||||
76 3.09130721617413 -3.411145687677366 0
|
||||
77 3.567287338332451 -3.439448189733019 0
|
||||
78 4.043963758935188 -3.462490104151034 0
|
||||
79 4.521595026340442 -3.482145270350558 0
|
||||
80 4.53664432455652 -1.978600022703204 0
|
||||
81 4.07646042959763 -1.955552774759151 0
|
||||
82 3.622473657533698 -1.929092238665446 0
|
||||
83 3.177227845469996 -1.897332899989072 0
|
||||
84 2.741992072859029 -1.858107555374913 0
|
||||
85 2.315943299837527 -1.808659406432935 0
|
||||
86 1.898361004221733 -1.745502414617874 0
|
||||
87 1.49145086264214 -1.664634388017854 0
|
||||
88 1.101775323220076 -1.562562613115749 0
|
||||
89 0.353776448027755 -2.251637070635797 0
|
||||
90 0.5817531180500853 -1.81803370667541 0
|
||||
91 5.893865315616141e-08 -3.688498271511504 0
|
||||
92 4.16589230598604e-08 -4.361309501422365 0
|
||||
93 -0.6217063447015869 -3.063796759607709 0
|
||||
94 -1.152592159125127 -3.183434038351562 0
|
||||
95 -1.651434963884719 -3.267053810340575 0
|
||||
96 -2.136365141575386 -3.328665661557225 0
|
||||
97 -2.61502807124587 -3.375240540152028 0
|
||||
98 -3.091306983305333 -3.411145327000243 0
|
||||
99 -3.567287131691835 -3.439447930570905 0
|
||||
100 -4.043963594137694 -3.462489938776664 0
|
||||
101 -4.521594918346396 -3.482145167593328 0
|
||||
102 -0.3537764846052657 -2.25163666228703 0
|
||||
103 -0.5817531278520051 -1.818033637502388 0
|
||||
104 -4.536644079337935 -1.978599647807268 0
|
||||
105 -4.076459846340027 -1.955551693229819 0
|
||||
106 -3.62247296968287 -1.929090872966037 0
|
||||
107 -3.177227175417933 -1.897331476171343 0
|
||||
108 -2.741991512175473 -1.858106255217367 0
|
||||
109 -2.315942871426326 -1.808658299363782 0
|
||||
110 -1.898360705274605 -1.745501533735374 0
|
||||
111 -1.491450676964205 -1.664633739553477 0
|
||||
112 -1.101775219860154 -1.562562160656453 0
|
||||
113 0.4257768789585238 -1.302334772873867 0
|
||||
114 0.1721672606205888 -1.17264922719834 0
|
||||
115 -0.4257768283604875 -1.302334697557239 0
|
||||
116 -0.1721671949129244 -1.172649169444562 0
|
||||
117 5.039529297540552e-08 -0.8795314788631696 0
|
||||
118 4.399248353403805e-08 -0.6140653765645073 0
|
||||
119 2.391773920807489e-08 -0.3141333775040666 0
|
||||
120 -0.8478847531712174 -1.078320812844798 0
|
||||
121 -0.9182559238223678 -0.7209963330169558 0
|
||||
122 -0.9651087839812744 -0.3619977550022322 0
|
||||
123 0.8478848525935928 -1.078321055602277 0
|
||||
124 0.9182560388765455 -0.7209965569029586 0
|
||||
125 0.9651088678468086 -0.3619978973464902 0
|
||||
126 0.8006710117138355 -2.47676624955276 0
|
||||
127 0.9687509599230363 -1.991490373658575 0
|
||||
128 1.267199118780179 -2.624331559216512 0
|
||||
129 1.386971133363275 -2.122717577561095 0
|
||||
130 1.733121009957757 -2.726002099497514 0
|
||||
131 1.819410451523041 -2.220141767339956 0
|
||||
132 2.196724740979399 -2.79935877814003 0
|
||||
133 2.258923990363529 -2.292999983602781 0
|
||||
134 2.659538340537992 -2.854063574089099 0
|
||||
135 2.703484150533682 -2.348367764794451 0
|
||||
136 3.123294379466398 -2.895966051131377 0
|
||||
137 3.153232485979804 -2.391299851091255 0
|
||||
138 3.589167673689383 -2.928956418122387 0
|
||||
139 3.608560261518269 -2.425426828148212 0
|
||||
140 4.057538435196422 -2.955867592895155 0
|
||||
141 4.069039111160356 -2.453487152690021 0
|
||||
142 4.528077399266859 -2.978924099167553 0
|
||||
143 4.533425113711518 -2.477698347634654 0
|
||||
144 0.5529057974518461 -3.729502166598679 0
|
||||
145 1.08019255813471 -3.784879970197416 0
|
||||
146 1.588026898054561 -3.835138377533823 0
|
||||
147 2.08405323877662 -3.876448947726767 0
|
||||
148 2.573544992310209 -3.909301557064001 0
|
||||
149 3.059798426647599 -3.93522570140068 0
|
||||
150 3.544781312614329 -3.955866580867792 0
|
||||
151 4.029558163742809 -3.972704995723558 0
|
||||
152 4.514598481900658 -3.98704568763109 0
|
||||
153 0.5207429539874712 -4.372050944340369 0
|
||||
154 1.034280493174569 -4.392852449659338 0
|
||||
155 1.539989110360686 -4.415218583983825 0
|
||||
156 2.039762094695762 -4.435343846034065 0
|
||||
157 2.535732622385146 -4.452137363307919 0
|
||||
158 3.029570711380306 -4.465715840583259 0
|
||||
159 3.522401092498168 -4.476640912585935 0
|
||||
160 4.014893345389466 -4.485580909772104 0
|
||||
161 4.507386261505489 -4.493183790689629 0
|
||||
162 -0.5529056692413425 -3.729501971224406 0
|
||||
163 -0.5207428615254834 -4.372050820213267 0
|
||||
164 -1.080192412465082 -3.784879670455047 0
|
||||
165 -1.034280383041777 -4.392852235768746 0
|
||||
166 -1.588026734554675 -3.835138038639476 0
|
||||
167 -1.539988984956153 -4.4152183290159 0
|
||||
168 -2.084053063491914 -3.876448618727172 0
|
||||
169 -2.039761962697679 -4.435343595363031 0
|
||||
170 -2.57354481360955 -3.909301269424566 0
|
||||
171 -2.535732489384031 -4.452137137805948 0
|
||||
172 -3.059798253458965 -3.935225467144031 0
|
||||
173 -3.02957058029087 -4.465715643196338 0
|
||||
174 -3.544781151325922 -3.955866397178196 0
|
||||
175 -3.52240096857565 -4.476640744295789 0
|
||||
176 -4.029558025213573 -3.97270485120735 0
|
||||
177 -4.014893239369709 -4.485580776942857 0
|
||||
178 -4.514598383857045 -3.987045581044113 0
|
||||
179 -4.507386188672238 -4.493183701962657 0
|
||||
180 -0.8006709540149735 -2.476765813610716 0
|
||||
181 -1.267198951523049 -2.624330912705657 0
|
||||
182 -1.733120766602745 -2.726001387393099 0
|
||||
183 -2.196724444010224 -2.799358049188522 0
|
||||
184 -2.659538010646273 -2.854062873235562 0
|
||||
185 -3.123294047769952 -2.895965440056678 0
|
||||
186 -3.589167377515512 -2.928955953414885 0
|
||||
187 -4.057538220541448 -2.955867327310949 0
|
||||
188 -4.528077277925624 -2.978923981788693 0
|
||||
189 -0.9687509231976139 -1.991490069922028 0
|
||||
190 -1.386970993902387 -2.122717016201259 0
|
||||
191 -1.819410201177847 -2.220141006425682 0
|
||||
192 -2.258923647273578 -2.292999103541908 0
|
||||
193 -2.703483736760897 -2.348366833255585 0
|
||||
194 -3.153232042234145 -2.391298962173866 0
|
||||
195 -3.608559855392381 -2.425426109249888 0
|
||||
196 -4.069038820050712 -2.453486730829595 0
|
||||
197 -4.533424988551208 -2.47769824111193 0
|
||||
198 -1.272476666753393e-08 -1.924359482524905 0
|
||||
199 -0.2533343974762239 -1.606443202429349 0
|
||||
200 0.2533344229837241 -1.606443132326576 0
|
||||
201 2.53249852051541e-08 -1.388593389582041 0
|
||||
202 -0.5272934750247175 -0.9926764285952894 0
|
||||
203 -0.5858028437947259 -0.6707709234636405 0
|
||||
204 -0.6157803404632994 -0.3382179577638268 0
|
||||
205 -0.2456717774258411 -0.9190691202974864 0
|
||||
206 -0.2802909995762338 -0.6309898154192547 0
|
||||
207 -0.2874733536873217 -0.3203514901649924 0
|
||||
208 0.5272935606350887 -0.9926765509995065 0
|
||||
209 0.2456718710348613 -0.9190692014700954 0
|
||||
210 0.5858029352058564 -0.6707710264887236 0
|
||||
211 0.2802910897441391 -0.6309898869990653 0
|
||||
212 0.6157804025422635 -0.3382180183964293 0
|
||||
213 0.2874734069247128 -0.3203515208733445 0
|
||||
214 -4.536347854639426 -1.481553666129692 0
|
||||
215 -4.530849447494669 -0.9863227548585151 0
|
||||
216 -4.518663750100603 -0.4925991230550557 0
|
||||
217 -4.076965269089723 -1.461824093896926 0
|
||||
218 -4.066822817654915 -0.9717378202251115 0
|
||||
219 -4.041844794305193 -0.4846493859382576 0
|
||||
220 -3.626631555456245 -1.439400263472837 0
|
||||
221 -3.614767853477897 -0.9552829042038593 0
|
||||
222 -3.577234059246457 -0.4756361204437503 0
|
||||
223 -3.190066245774652 -1.412949304825956 0
|
||||
224 -3.183592242159449 -0.9362633794796755 0
|
||||
225 -3.139804799022206 -0.465366666148178 0
|
||||
226 -2.770239834557847 -1.381149002386727 0
|
||||
227 -2.78361605771829 -0.9144286373168963 0
|
||||
228 -2.777303799227714 -0.454899765247956 0
|
||||
229 -2.363294730094991 -1.342156913093806 0
|
||||
230 -2.400494437457917 -0.8884888000889807 0
|
||||
231 -2.434740206505228 -0.4429058760892239 0
|
||||
232 -1.964720377034965 -1.293667708472078 0
|
||||
233 -2.01933996144247 -0.8565871950067101 0
|
||||
234 -2.068624514239478 -0.4275156525307307 0
|
||||
235 -1.575416930632774 -1.233626257664419 0
|
||||
236 -1.64166735878701 -0.8176813599726269 0
|
||||
237 -1.697012105595388 -0.4086248381798738 0
|
||||
238 -1.200429025084152 -1.161147932262043 0
|
||||
239 -1.272506329260207 -0.7718009641075847 0
|
||||
240 -1.327401030882088 -0.386422544456637 0
|
||||
241 4.536348463925029 -1.481554877027547 0
|
||||
242 4.076966575441157 -1.461826747345742 0
|
||||
243 3.626632758299004 -1.439402719821685 0
|
||||
244 3.190067289609639 -1.412951514178275 0
|
||||
245 2.770240603008739 -1.381150766970896 0
|
||||
246 2.363295268598255 -1.342158273522384 0
|
||||
247 1.964720745715605 -1.29366873340107 0
|
||||
248 1.575417171293567 -1.233626983666225 0
|
||||
249 1.200429174776261 -1.161148389883658 0
|
||||
250 4.530851405729067 -0.9863270776419871 0
|
||||
251 4.066826269903716 -0.9717448686110475 0
|
||||
252 3.614769893474459 -0.9552868751328539 0
|
||||
253 3.183593918032332 -0.9362666689585752 0
|
||||
254 2.783617050253483 -0.9144308326138045 0
|
||||
255 2.400495065388643 -0.8884903509228984 0
|
||||
256 2.019340386429411 -0.856588326996137 0
|
||||
257 1.641667642233049 -0.8176821443214574 0
|
||||
258 1.272506501026669 -0.771801400260968 0
|
||||
259 4.518663549413066 -0.4925982624014307 0
|
||||
260 4.04184693132522 -0.4846532618590358 0
|
||||
261 3.577237250381986 -0.4756414769154902 0
|
||||
262 3.139808324752386 -0.4653725616385991 0
|
||||
263 2.777304908988505 -0.454902208732857 0
|
||||
264 2.434740771273721 -0.4429073253123982 0
|
||||
265 2.068624882856776 -0.4275166319116776 0
|
||||
266 1.697012350109042 -0.4086254890422338 0
|
||||
267 1.327401164359004 -0.3864228466778788 0
|
||||
$EndNodes
|
||||
$Elements
|
||||
279
|
||||
1 1 2 1 10 1 11
|
||||
2 1 2 1 10 11 12
|
||||
3 1 2 1 10 12 13
|
||||
4 1 2 1 10 13 14
|
||||
5 1 2 1 10 14 15
|
||||
6 1 2 1 10 15 16
|
||||
7 1 2 1 10 16 17
|
||||
8 1 2 1 10 17 18
|
||||
9 1 2 1 10 18 19
|
||||
10 1 2 1 10 19 20
|
||||
11 1 2 1 10 20 21
|
||||
12 1 2 1 10 21 22
|
||||
13 1 2 1 10 22 23
|
||||
14 1 2 1 10 23 24
|
||||
15 1 2 1 10 24 25
|
||||
16 1 2 1 10 25 26
|
||||
17 1 2 1 10 26 27
|
||||
18 1 2 1 10 27 28
|
||||
19 1 2 1 10 28 29
|
||||
20 1 2 1 10 29 2
|
||||
21 1 2 2 11 2 30
|
||||
22 1 2 2 11 30 31
|
||||
23 1 2 2 11 31 32
|
||||
24 1 2 2 11 32 33
|
||||
25 1 2 2 11 33 34
|
||||
26 1 2 2 11 34 35
|
||||
27 1 2 2 11 35 36
|
||||
28 1 2 2 11 36 37
|
||||
29 1 2 2 11 37 38
|
||||
30 1 2 2 11 38 3
|
||||
31 1 2 3 13 4 42
|
||||
32 1 2 3 13 42 5
|
||||
33 1 2 4 15 6 48
|
||||
34 1 2 4 15 48 7
|
||||
35 1 2 5 17 8 54
|
||||
36 1 2 5 17 54 9
|
||||
37 1 2 6 19 10 58
|
||||
38 1 2 6 19 58 59
|
||||
39 1 2 6 19 59 60
|
||||
40 1 2 6 19 60 61
|
||||
41 1 2 6 19 61 62
|
||||
42 1 2 6 19 62 63
|
||||
43 1 2 6 19 63 64
|
||||
44 1 2 6 19 64 65
|
||||
45 1 2 6 19 65 66
|
||||
46 1 2 6 19 66 1
|
||||
47 3 2 1 1 67 71 126 89
|
||||
48 3 2 1 1 89 126 127 90
|
||||
49 3 2 1 1 90 127 88 68
|
||||
50 3 2 1 1 71 72 128 126
|
||||
51 3 2 1 1 126 128 129 127
|
||||
52 3 2 1 1 127 129 87 88
|
||||
53 3 2 1 1 72 73 130 128
|
||||
54 3 2 1 1 128 130 131 129
|
||||
55 3 2 1 1 129 131 86 87
|
||||
56 3 2 1 1 73 74 132 130
|
||||
57 3 2 1 1 130 132 133 131
|
||||
58 3 2 1 1 131 133 85 86
|
||||
59 3 2 1 1 74 75 134 132
|
||||
60 3 2 1 1 132 134 135 133
|
||||
61 3 2 1 1 133 135 84 85
|
||||
62 3 2 1 1 75 76 136 134
|
||||
63 3 2 1 1 134 136 137 135
|
||||
64 3 2 1 1 135 137 83 84
|
||||
65 3 2 1 1 76 77 138 136
|
||||
66 3 2 1 1 136 138 139 137
|
||||
67 3 2 1 1 137 139 82 83
|
||||
68 3 2 1 1 77 78 140 138
|
||||
69 3 2 1 1 138 140 141 139
|
||||
70 3 2 1 1 139 141 81 82
|
||||
71 3 2 1 1 78 79 142 140
|
||||
72 3 2 1 1 140 142 143 141
|
||||
73 3 2 1 1 141 143 80 81
|
||||
74 3 2 1 1 79 32 33 142
|
||||
75 3 2 1 1 142 33 34 143
|
||||
76 3 2 1 1 143 34 35 80
|
||||
77 3 2 1 1 67 91 144 71
|
||||
78 3 2 1 1 71 144 145 72
|
||||
79 3 2 1 1 72 145 146 73
|
||||
80 3 2 1 1 73 146 147 74
|
||||
81 3 2 1 1 74 147 148 75
|
||||
82 3 2 1 1 75 148 149 76
|
||||
83 3 2 1 1 76 149 150 77
|
||||
84 3 2 1 1 77 150 151 78
|
||||
85 3 2 1 1 78 151 152 79
|
||||
86 3 2 1 1 79 152 31 32
|
||||
87 3 2 1 1 91 92 153 144
|
||||
88 3 2 1 1 144 153 154 145
|
||||
89 3 2 1 1 145 154 155 146
|
||||
90 3 2 1 1 146 155 156 147
|
||||
91 3 2 1 1 147 156 157 148
|
||||
92 3 2 1 1 148 157 158 149
|
||||
93 3 2 1 1 149 158 159 150
|
||||
94 3 2 1 1 150 159 160 151
|
||||
95 3 2 1 1 151 160 161 152
|
||||
96 3 2 1 1 152 161 30 31
|
||||
97 3 2 1 1 92 20 21 153
|
||||
98 3 2 1 1 153 21 22 154
|
||||
99 3 2 1 1 154 22 23 155
|
||||
100 3 2 1 1 155 23 24 156
|
||||
101 3 2 1 1 156 24 25 157
|
||||
102 3 2 1 1 157 25 26 158
|
||||
103 3 2 1 1 158 26 27 159
|
||||
104 3 2 1 1 159 27 28 160
|
||||
105 3 2 1 1 160 28 29 161
|
||||
106 3 2 1 1 161 29 2 30
|
||||
107 3 2 1 1 67 93 162 91
|
||||
108 3 2 1 1 91 162 163 92
|
||||
109 3 2 1 1 92 163 19 20
|
||||
110 3 2 1 1 93 94 164 162
|
||||
111 3 2 1 1 162 164 165 163
|
||||
112 3 2 1 1 163 165 18 19
|
||||
113 3 2 1 1 94 95 166 164
|
||||
114 3 2 1 1 164 166 167 165
|
||||
115 3 2 1 1 165 167 17 18
|
||||
116 3 2 1 1 95 96 168 166
|
||||
117 3 2 1 1 166 168 169 167
|
||||
118 3 2 1 1 167 169 16 17
|
||||
119 3 2 1 1 96 97 170 168
|
||||
120 3 2 1 1 168 170 171 169
|
||||
121 3 2 1 1 169 171 15 16
|
||||
122 3 2 1 1 97 98 172 170
|
||||
123 3 2 1 1 170 172 173 171
|
||||
124 3 2 1 1 171 173 14 15
|
||||
125 3 2 1 1 98 99 174 172
|
||||
126 3 2 1 1 172 174 175 173
|
||||
127 3 2 1 1 173 175 13 14
|
||||
128 3 2 1 1 99 100 176 174
|
||||
129 3 2 1 1 174 176 177 175
|
||||
130 3 2 1 1 175 177 12 13
|
||||
131 3 2 1 1 100 101 178 176
|
||||
132 3 2 1 1 176 178 179 177
|
||||
133 3 2 1 1 177 179 11 12
|
||||
134 3 2 1 1 101 64 65 178
|
||||
135 3 2 1 1 178 65 66 179
|
||||
136 3 2 1 1 179 66 1 11
|
||||
137 3 2 1 1 67 102 180 93
|
||||
138 3 2 1 1 93 180 181 94
|
||||
139 3 2 1 1 94 181 182 95
|
||||
140 3 2 1 1 95 182 183 96
|
||||
141 3 2 1 1 96 183 184 97
|
||||
142 3 2 1 1 97 184 185 98
|
||||
143 3 2 1 1 98 185 186 99
|
||||
144 3 2 1 1 99 186 187 100
|
||||
145 3 2 1 1 100 187 188 101
|
||||
146 3 2 1 1 101 188 63 64
|
||||
147 3 2 1 1 102 103 189 180
|
||||
148 3 2 1 1 180 189 190 181
|
||||
149 3 2 1 1 181 190 191 182
|
||||
150 3 2 1 1 182 191 192 183
|
||||
151 3 2 1 1 183 192 193 184
|
||||
152 3 2 1 1 184 193 194 185
|
||||
153 3 2 1 1 185 194 195 186
|
||||
154 3 2 1 1 186 195 196 187
|
||||
155 3 2 1 1 187 196 197 188
|
||||
156 3 2 1 1 188 197 62 63
|
||||
157 3 2 1 1 103 69 112 189
|
||||
158 3 2 1 1 189 112 111 190
|
||||
159 3 2 1 1 190 111 110 191
|
||||
160 3 2 1 1 191 110 109 192
|
||||
161 3 2 1 1 192 109 108 193
|
||||
162 3 2 1 1 193 108 107 194
|
||||
163 3 2 1 1 194 107 106 195
|
||||
164 3 2 1 1 195 106 105 196
|
||||
165 3 2 1 1 196 105 104 197
|
||||
166 3 2 1 1 197 104 61 62
|
||||
167 3 2 1 1 67 89 198 102
|
||||
168 3 2 1 1 102 198 199 103
|
||||
169 3 2 1 1 103 199 115 69
|
||||
170 3 2 1 1 89 90 200 198
|
||||
171 3 2 1 1 198 200 201 199
|
||||
172 3 2 1 1 199 201 116 115
|
||||
173 3 2 1 1 90 68 113 200
|
||||
174 3 2 1 1 200 113 114 201
|
||||
175 3 2 1 1 201 114 70 116
|
||||
176 3 2 1 1 69 115 202 120
|
||||
177 3 2 1 1 120 202 203 121
|
||||
178 3 2 1 1 121 203 204 122
|
||||
179 3 2 1 1 122 204 49 50
|
||||
180 3 2 1 1 115 116 205 202
|
||||
181 3 2 1 1 202 205 206 203
|
||||
182 3 2 1 1 203 206 207 204
|
||||
183 3 2 1 1 204 207 7 49
|
||||
184 3 2 1 1 116 70 117 205
|
||||
185 3 2 1 1 205 117 118 206
|
||||
186 3 2 1 1 206 118 119 207
|
||||
187 3 2 1 1 207 119 48 7
|
||||
188 3 2 1 1 68 123 208 113
|
||||
189 3 2 1 1 113 208 209 114
|
||||
190 3 2 1 1 114 209 117 70
|
||||
191 3 2 1 1 123 124 210 208
|
||||
192 3 2 1 1 208 210 211 209
|
||||
193 3 2 1 1 209 211 118 117
|
||||
194 3 2 1 1 124 125 212 210
|
||||
195 3 2 1 1 210 212 213 211
|
||||
196 3 2 1 1 211 213 119 118
|
||||
197 3 2 1 1 125 46 47 212
|
||||
198 3 2 1 1 212 47 6 213
|
||||
199 3 2 1 1 213 6 48 119
|
||||
200 3 2 1 1 61 104 214 60
|
||||
201 3 2 1 1 60 214 215 59
|
||||
202 3 2 1 1 59 215 216 58
|
||||
203 3 2 1 1 58 216 57 10
|
||||
204 3 2 1 1 104 105 217 214
|
||||
205 3 2 1 1 214 217 218 215
|
||||
206 3 2 1 1 215 218 219 216
|
||||
207 3 2 1 1 216 219 56 57
|
||||
208 3 2 1 1 105 106 220 217
|
||||
209 3 2 1 1 217 220 221 218
|
||||
210 3 2 1 1 218 221 222 219
|
||||
211 3 2 1 1 219 222 55 56
|
||||
212 3 2 1 1 106 107 223 220
|
||||
213 3 2 1 1 220 223 224 221
|
||||
214 3 2 1 1 221 224 225 222
|
||||
215 3 2 1 1 222 225 9 55
|
||||
216 3 2 1 1 107 108 226 223
|
||||
217 3 2 1 1 223 226 227 224
|
||||
218 3 2 1 1 224 227 228 225
|
||||
219 3 2 1 1 225 228 54 9
|
||||
220 3 2 1 1 108 109 229 226
|
||||
221 3 2 1 1 226 229 230 227
|
||||
222 3 2 1 1 227 230 231 228
|
||||
223 3 2 1 1 228 231 8 54
|
||||
224 3 2 1 1 109 110 232 229
|
||||
225 3 2 1 1 229 232 233 230
|
||||
226 3 2 1 1 230 233 234 231
|
||||
227 3 2 1 1 231 234 53 8
|
||||
228 3 2 1 1 110 111 235 232
|
||||
229 3 2 1 1 232 235 236 233
|
||||
230 3 2 1 1 233 236 237 234
|
||||
231 3 2 1 1 234 237 52 53
|
||||
232 3 2 1 1 111 112 238 235
|
||||
233 3 2 1 1 235 238 239 236
|
||||
234 3 2 1 1 236 239 240 237
|
||||
235 3 2 1 1 237 240 51 52
|
||||
236 3 2 1 1 112 69 120 238
|
||||
237 3 2 1 1 238 120 121 239
|
||||
238 3 2 1 1 239 121 122 240
|
||||
239 3 2 1 1 240 122 50 51
|
||||
240 3 2 1 1 35 36 241 80
|
||||
241 3 2 1 1 80 241 242 81
|
||||
242 3 2 1 1 81 242 243 82
|
||||
243 3 2 1 1 82 243 244 83
|
||||
244 3 2 1 1 83 244 245 84
|
||||
245 3 2 1 1 84 245 246 85
|
||||
246 3 2 1 1 85 246 247 86
|
||||
247 3 2 1 1 86 247 248 87
|
||||
248 3 2 1 1 87 248 249 88
|
||||
249 3 2 1 1 88 249 123 68
|
||||
250 3 2 1 1 36 37 250 241
|
||||
251 3 2 1 1 241 250 251 242
|
||||
252 3 2 1 1 242 251 252 243
|
||||
253 3 2 1 1 243 252 253 244
|
||||
254 3 2 1 1 244 253 254 245
|
||||
255 3 2 1 1 245 254 255 246
|
||||
256 3 2 1 1 246 255 256 247
|
||||
257 3 2 1 1 247 256 257 248
|
||||
258 3 2 1 1 248 257 258 249
|
||||
259 3 2 1 1 249 258 124 123
|
||||
260 3 2 1 1 37 38 259 250
|
||||
261 3 2 1 1 250 259 260 251
|
||||
262 3 2 1 1 251 260 261 252
|
||||
263 3 2 1 1 252 261 262 253
|
||||
264 3 2 1 1 253 262 263 254
|
||||
265 3 2 1 1 254 263 264 255
|
||||
266 3 2 1 1 255 264 265 256
|
||||
267 3 2 1 1 256 265 266 257
|
||||
268 3 2 1 1 257 266 267 258
|
||||
269 3 2 1 1 258 267 125 124
|
||||
270 3 2 1 1 38 3 39 259
|
||||
271 3 2 1 1 259 39 40 260
|
||||
272 3 2 1 1 260 40 41 261
|
||||
273 3 2 1 1 261 41 4 262
|
||||
274 3 2 1 1 262 4 42 263
|
||||
275 3 2 1 1 263 42 5 264
|
||||
276 3 2 1 1 264 5 43 265
|
||||
277 3 2 1 1 265 43 44 266
|
||||
278 3 2 1 1 266 44 45 267
|
||||
279 3 2 1 1 267 45 46 125
|
||||
$EndElements
|
||||
@@ -0,0 +1,841 @@
|
||||
$MeshFormat
|
||||
2.2 0 8
|
||||
$EndMeshFormat
|
||||
$Nodes
|
||||
284
|
||||
1 -5 -5 0
|
||||
2 5 -5 0
|
||||
3 5 0 0
|
||||
4 3 0 0
|
||||
5 2.5 0 0
|
||||
6 0.25 0 0
|
||||
7 -0.25 0 0
|
||||
8 -2.5 0 0
|
||||
9 -3 0 0
|
||||
10 -5 0 0
|
||||
11 -4.5 -5 0
|
||||
12 -4 -5 0
|
||||
13 -3.5 -5 0
|
||||
14 -3 -5 0
|
||||
15 -2.5 -5 0
|
||||
16 -2 -5 0
|
||||
17 -1.5 -5 0
|
||||
18 -1 -5 0
|
||||
19 -0.5 -5 0
|
||||
20 0 -5 0
|
||||
21 0.5 -5 0
|
||||
22 1 -5 0
|
||||
23 1.5 -5 0
|
||||
24 2 -5 0
|
||||
25 2.5 -5 0
|
||||
26 3 -5 0
|
||||
27 3.5 -5 0
|
||||
28 4 -5 0
|
||||
29 4.5 -5 0
|
||||
30 5 -4.5 0
|
||||
31 5 -4 0
|
||||
32 5 -3.5 0
|
||||
33 5 -3 0
|
||||
34 5 -2.5 0
|
||||
35 5 -2 0
|
||||
36 5 -1.5 0
|
||||
37 5 -1 0
|
||||
38 5 -0.5 0
|
||||
39 4.5 0 0
|
||||
40 4 0 0
|
||||
41 3.5 0 0
|
||||
42 2.05 0 0
|
||||
43 1.6 0 0
|
||||
44 1.15 0 0
|
||||
45 0.7 0 0
|
||||
46 -0.7 0 0
|
||||
47 -1.15 0 0
|
||||
48 -1.6 0 0
|
||||
49 -2.05 0 0
|
||||
50 -3.5 0 0
|
||||
51 -4 0 0
|
||||
52 -4.5 0 0
|
||||
53 -5 -0.5 0
|
||||
54 -5 -1 0
|
||||
55 -5 -1.5 0
|
||||
56 -5 -2 0
|
||||
57 -5 -2.5 0
|
||||
58 -5 -3 0
|
||||
59 -5 -3.5 0
|
||||
60 -5 -4 0
|
||||
61 -5 -4.5 0
|
||||
62 2.281579424776069 -0.3939927528378067 0
|
||||
63 0.9247461078594561 -0.4010584732281242 0
|
||||
64 -2.251733651700584 -0.4058752540090222 0
|
||||
65 -0.9249999999999999 -0.3860346567053154 0
|
||||
66 -0.25 -4.566987298107781 0
|
||||
67 -1.75 -4.566987298107781 0
|
||||
68 1.757138123938968 -4.561388469215611 0
|
||||
69 -4.57663533780044 -2.749996588117569 0
|
||||
70 4.574218761575537 -2.744193863029555 0
|
||||
71 -3.261047720525324 -4.633492158122827 0
|
||||
72 3.233878916273093 -4.572861684713071 0
|
||||
73 3.249983936371643 -0.4138167322607993 0
|
||||
74 -3.22796127783195 -0.4745514342555692 0
|
||||
75 0 -0.3897114317029975 0
|
||||
76 0.7668085767464438 -4.554291553533576 0
|
||||
77 4.524628320503154 -1.71259283597443 0
|
||||
78 -4.57425038881721 -1.740509953296104 0
|
||||
79 4.657280536213934 -3.745352555127771 0
|
||||
80 -4.561557444591958 -3.743801811882471 0
|
||||
81 4.247947141075545 -0.413931595411861 0
|
||||
82 -4.25 -0.4330127018922192 0
|
||||
83 -4.231971247749111 -4.526000871317382 0
|
||||
84 4.247882011655604 -4.578053652047846 0
|
||||
85 0.459240288050884 -0.4011062233317819 0
|
||||
86 0.2191981222082625 -0.7894052414358377 0
|
||||
87 -0.2550018029202653 -0.7696812455915785 0
|
||||
88 -0.03974194597926101 -1.173522830121104 0
|
||||
89 0.4346364211584371 -1.192775566160588 0
|
||||
90 0.1864311373004761 -1.586413475031659 0
|
||||
91 0.6507331103481842 -1.602794886574206 0
|
||||
92 -0.2962063372658213 -1.560534711371811 0
|
||||
93 0.4052226136760305 -1.998915829261101 0
|
||||
94 0.8747101368415163 -2.014294995428312 0
|
||||
95 0.6254442827334447 -2.414070730277764 0
|
||||
96 1.096931360307092 -2.429431309425559 0
|
||||
97 1.346300816143899 -2.027816295003789 0
|
||||
98 1.569865887277439 -2.444122401305898 0
|
||||
99 1.819907460974127 -2.038729916981179 0
|
||||
100 2.044972612829793 -2.458848886499749 0
|
||||
101 0.1536392683070031 -2.397215233401116 0
|
||||
102 2.296563295216953 -2.050400407953151 0
|
||||
103 2.523676371701205 -2.474026874448667 0
|
||||
104 2.775089986948031 -2.064883565181495 0
|
||||
105 0.3753978099944599 -2.816445321672421 0
|
||||
106 -0.101594819363816 -2.799164055357317 0
|
||||
107 1.321674174422592 -2.846267815879844 0
|
||||
108 2.270294778623243 -2.882886829258818 0
|
||||
109 2.752962034536527 -2.899399230379248 0
|
||||
110 1.594627579321902 -1.620863532351973 0
|
||||
111 2.496530594790508 -3.311620345179397 0
|
||||
112 2.983571088917511 -3.327819126997308 0
|
||||
113 0.1137225221607727 -3.227426914851572 0
|
||||
114 -0.3711032418985809 -3.207832251592655 0
|
||||
115 -0.5828344401467249 -2.781198932956502 0
|
||||
116 -0.8353259116793657 -3.185936459987706 0
|
||||
117 -1.059979017425433 -2.762083576904867 0
|
||||
118 -1.314767961260485 -3.167209797744215 0
|
||||
119 -1.538368860989399 -2.742671230707596 0
|
||||
120 -1.793679531768132 -3.151042894839176 0
|
||||
121 -2.01924352863926 -2.723529808724752 0
|
||||
122 -1.762667957890364 -2.316647156105704 0
|
||||
123 -2.242107472177446 -2.296152128033975 0
|
||||
124 -2.502564281105353 -2.703893235067956 0
|
||||
125 -2.725331546445376 -2.27365855104896 0
|
||||
126 -2.987889362306672 -2.683287954718313 0
|
||||
127 -1.983692675350436 -1.892945890847701 0
|
||||
128 -2.763007610522606 -3.115691622644485 0
|
||||
129 -3.251879284051252 -3.095617007538864 0
|
||||
130 -3.21286057170471 -2.250678390947021 0
|
||||
131 -2.949339074017358 -1.839974815980651 0
|
||||
132 3.239407644843084 -2.914212985735978 0
|
||||
133 2.547241608329427 -1.641063282475632 0
|
||||
134 3.028142281477624 -1.652825101445818 0
|
||||
135 -1.510406177790435 -1.913844107670896 0
|
||||
136 -1.730236214842076 -1.505967151038945 0
|
||||
137 -1.091336732540506 -3.593626264859387 0
|
||||
138 -3.421854185011473 -1.808744480608429 0
|
||||
139 3.472898669730996 -3.34330809310189 0
|
||||
140 -1.256990758306386 -1.517443982643851 0
|
||||
141 -3.024894304094996 -3.530456033168236 0
|
||||
142 -3.516854524302131 -3.510343830944479 0
|
||||
143 -1.03266707250538 -1.934584786094152 0
|
||||
144 -2.201596179944664 -1.480657719683199 0
|
||||
145 0.9021516016281497 -1.202776512596341 0
|
||||
146 2.725588881466229 -3.738813882257535 0
|
||||
147 -2.53516560562837 -3.550136689533193 0
|
||||
148 -1.44581323125898 -1.123897963936046 0
|
||||
149 2.235681770010328 -3.726033843511675 0
|
||||
150 3.259277130723387 -2.077185219746537 0
|
||||
151 3.513246676438002 -1.662021998760507 0
|
||||
152 3.758409946183188 -2.082693761823018 0
|
||||
153 0.5991111801411881 -3.240104427542418 0
|
||||
154 0.3369992297204003 -3.654513295976306 0
|
||||
155 0.8290255688225511 -3.666471451892976 0
|
||||
156 -3.722265329818222 -2.240608173907041 0
|
||||
157 -3.171684897998753 -1.403346357255416 0
|
||||
158 -3.644197255924033 -1.372619186245074 0
|
||||
159 2.794592285631932 -1.233756575634245 0
|
||||
160 2.318227283889231 -1.220216315858479 0
|
||||
161 -0.9783790307780148 -1.130197912479716 0
|
||||
162 -3.713876128115184 -3.079033658054799 0
|
||||
163 3.742324371372884 -2.928999279490477 0
|
||||
164 2.485083477357409 -4.14729896608471 0
|
||||
165 -2.778145642709027 -3.956644572916525 0
|
||||
166 -2.304612039225867 -3.9863489431438 0
|
||||
167 3.966968926084867 -3.336303597578833 0
|
||||
168 3.706083502027171 -3.750175369113853 0
|
||||
169 -4.077410641740634 -3.501439180006514 0
|
||||
170 -3.76933168214444 -3.914132682209269 0
|
||||
171 -2.568167113352794 -4.403628334893856 0
|
||||
172 3.999000211194053 -1.669214607229196 0
|
||||
173 3.757262029468758 -1.246079492069315 0
|
||||
174 4.257624135642195 -1.243021671724434 0
|
||||
175 -3.406533611304276 -0.962718897978591 0
|
||||
176 -2.86976350923752 -0.9741763965199361 0
|
||||
177 -3.887783979793185 -0.9468337725874192 0
|
||||
178 -4.151590072554236 -1.433655682692203 0
|
||||
179 2.567375777521253 -0.8162946823393982 0
|
||||
180 -2.710501565840532 -0.449194289380908 0
|
||||
181 -2.361189298792369 -0.8166861741006153 0
|
||||
182 -1.992682834578591 -0.6868709027668873 0
|
||||
183 0.5758835407852954 -4.076507624142248 0
|
||||
184 1.063684706252736 -4.093379787905304 0
|
||||
185 1.307846713632664 -3.678677225448682 0
|
||||
186 0.06794786130557443 -4.080395137507568 0
|
||||
187 -2.678398828890455 -1.460184324491551 0
|
||||
188 -0.8061658045868053 -2.358239065659463 0
|
||||
189 -4.417190667034111 -0.9530981038866508 0
|
||||
190 -3.73616854160164 -0.4667243592037824 0
|
||||
191 2.068162362263069 -1.631550568057341 0
|
||||
192 1.835382136049844 -1.217564469998849 0
|
||||
193 2.076433566739975 -0.8129459415658435 0
|
||||
194 1.61110337936101 -0.8034909695554286 0
|
||||
195 1.370865405519029 -1.205639238805478 0
|
||||
196 1.266721313972774 -4.554232785919753 0
|
||||
197 1.152132432870027 -0.7985062781329273 0
|
||||
198 3.748778721192141 -0.4141047359259749 0
|
||||
199 -0.3239021359339606 -2.378890209329921 0
|
||||
200 0.2607890790366596 -4.560067219429725 0
|
||||
201 -0.4362762851344742 -4.076426953781812 0
|
||||
202 -0.7421904022893758 -4.524993181824712 0
|
||||
203 -1.253691857241304 -4.549910310088686 0
|
||||
204 -1.488540618385916 -4.054529834302866 0
|
||||
205 1.541536749303611 -4.112783475173385 0
|
||||
206 -0.5479417186259372 -1.958200565306824 0
|
||||
207 -0.7713233681764451 -1.536925116417346 0
|
||||
208 -0.5067754141866345 -1.151982463020497 0
|
||||
209 -2.463410962804289 -1.87392890501434 0
|
||||
210 -1.285472530570352 -2.338305660158975 0
|
||||
211 1.378798590411674 -0.3978505940829993 0
|
||||
212 4.568566441173747 -2.253723730329266 0
|
||||
213 4.088512427247694 -2.504191622867672 0
|
||||
214 -4.571120642477398 -2.248470981336947 0
|
||||
215 -4.173258424046725 -2.492769868254391 0
|
||||
216 -2.047412941544885 -3.538687166079191 0
|
||||
217 1.125824650205117 -1.614348493757459 0
|
||||
218 -4.567172963194309 -3.249678418636941 0
|
||||
219 4.501762177208068 -3.291802327374498 0
|
||||
220 2.734404034710506 -4.574544109781256 0
|
||||
221 3.737632412102343 -4.577017677939905 0
|
||||
222 -3.747757947362715 -4.608888775351113 0
|
||||
223 0.6858343131338018 -0.7970681284062564 0
|
||||
224 2.015439853666996 -3.2910097832621 0
|
||||
225 -1.370883691544074 -0.3644617263994698 0
|
||||
226 -0.4732994730374375 -0.3853059645196071 0
|
||||
227 -4.170675295358781 -1.983559227818775 0
|
||||
228 -2.277544785346445 -3.130935475978483 0
|
||||
229 -3.263718927452331 -3.912011456955563 0
|
||||
230 -0.1542685254652809 -3.640226549090094 0
|
||||
231 -3.454561268417778 -2.67774506671365 0
|
||||
232 3.214928094260674 -3.743707595789564 0
|
||||
233 3.007108075975173 -2.488409339011342 0
|
||||
234 2.750375637050162 -0.4010587521556839 0
|
||||
235 3.269904368436389 -1.242599673877227 0
|
||||
236 0.8478171295042514 -2.831115790911858 0
|
||||
237 -0.0704595287570349 -1.980028337283739 0
|
||||
238 1.793648233980072 -2.86032311009413 0
|
||||
239 3.515839932724234 -2.499282034779171 0
|
||||
240 1.07506868241603 -3.253368514666169 0
|
||||
241 -1.567508338488392 -3.57138356920831 0
|
||||
242 -0.9414895224228255 -4.065708547614685 0
|
||||
243 1.832985826881454 -0.4013800430070131 0
|
||||
244 1.547665348273904 -3.266345147264329 0
|
||||
245 2.24775968641297 -4.567837723912959 0
|
||||
246 3.024151650683252 -0.8281976201130985 0
|
||||
247 3.975810147196631 -4.172641805166976 0
|
||||
248 3.503728254202536 -0.8279311588352849 0
|
||||
249 4.019169941690009 -0.8158447756611453 0
|
||||
250 -4.60633771632638 -4.233723530566535 0
|
||||
251 4.538799499276811 -4.205405374535824 0
|
||||
252 4.562305311857926 -0.7843140484770377 0
|
||||
253 2.972986107264538 -4.155124335587413 0
|
||||
254 1.778207995839603 -3.692155396798268 0
|
||||
255 -0.6383000365235054 -3.628292837821056 0
|
||||
256 2.007567967143815 -4.134582979116102 0
|
||||
257 3.473553196520742 -4.16192141138513 0
|
||||
258 -0.7147426201537252 -0.7593778023334516 0
|
||||
259 -1.806767054514615 -0.3633765595208169 0
|
||||
260 -4.167097947097267 -2.975013458090675 0
|
||||
261 4.175407739112876 -2.963681551184706 0
|
||||
262 -1.168353453833206 -0.7478369226339197 0
|
||||
263 -4.628606225681111 -1.325452747974991 0
|
||||
264 -1.955754170223746 -1.109359237158393 0
|
||||
265 -2.181031249640995 -4.584142759217013 0
|
||||
266 4.182701247812142 -2.054804320229848 0
|
||||
267 -4.210523009401651 -3.998563903983795 0
|
||||
268 4.199775238004059 -3.750658239585041 0
|
||||
269 -1.916827765372219 -4.194625820537434 0
|
||||
270 -1.592821679170089 -0.7265436064032613 0
|
||||
271 4.67022823811257 -1.25 0
|
||||
272 -3.831566914438898 -1.745340707425962 0
|
||||
273 -4.63781179938241 -0.6091765096601387 0
|
||||
274 -3.039941547144599 -4.302681803322779 0
|
||||
275 -3.505011562055658 -4.271356754644082 0
|
||||
276 -4.633974596215562 -4.633974596215562 0
|
||||
277 4.633974596215562 -4.633974596215562 0
|
||||
278 4.636520367722471 -0.3634796322775284 0
|
||||
279 -2.418900170891447 -1.187862709206192 0
|
||||
280 -2.873831276204544 -4.667960459267892 0
|
||||
281 -3.842482079912878 -2.704892577303744 0
|
||||
282 -3.908734077731807 -4.261830041615932 0
|
||||
283 -1.821328914771955 -3.849262003031382 0
|
||||
284 -4.705859493625653 -0.2869977523880989 0
|
||||
$EndNodes
|
||||
$Elements
|
||||
548
|
||||
1 1 2 1 10 1 11
|
||||
2 1 2 1 10 11 12
|
||||
3 1 2 1 10 12 13
|
||||
4 1 2 1 10 13 14
|
||||
5 1 2 1 10 14 15
|
||||
6 1 2 1 10 15 16
|
||||
7 1 2 1 10 16 17
|
||||
8 1 2 1 10 17 18
|
||||
9 1 2 1 10 18 19
|
||||
10 1 2 1 10 19 20
|
||||
11 1 2 1 10 20 21
|
||||
12 1 2 1 10 21 22
|
||||
13 1 2 1 10 22 23
|
||||
14 1 2 1 10 23 24
|
||||
15 1 2 1 10 24 25
|
||||
16 1 2 1 10 25 26
|
||||
17 1 2 1 10 26 27
|
||||
18 1 2 1 10 27 28
|
||||
19 1 2 1 10 28 29
|
||||
20 1 2 1 10 29 2
|
||||
21 1 2 2 11 2 30
|
||||
22 1 2 2 11 30 31
|
||||
23 1 2 2 11 31 32
|
||||
24 1 2 2 11 32 33
|
||||
25 1 2 2 11 33 34
|
||||
26 1 2 2 11 34 35
|
||||
27 1 2 2 11 35 36
|
||||
28 1 2 2 11 36 37
|
||||
29 1 2 2 11 37 38
|
||||
30 1 2 2 11 38 3
|
||||
31 1 2 3 13 4 5
|
||||
32 1 2 4 15 6 7
|
||||
33 1 2 5 17 8 9
|
||||
34 1 2 6 19 10 53
|
||||
35 1 2 6 19 53 54
|
||||
36 1 2 6 19 54 55
|
||||
37 1 2 6 19 55 56
|
||||
38 1 2 6 19 56 57
|
||||
39 1 2 6 19 57 58
|
||||
40 1 2 6 19 58 59
|
||||
41 1 2 6 19 59 60
|
||||
42 1 2 6 19 60 61
|
||||
43 1 2 6 19 61 1
|
||||
44 2 2 1 1 137 241 204
|
||||
45 2 2 1 1 204 242 137
|
||||
46 2 2 1 1 175 176 74
|
||||
47 2 2 1 1 176 180 74
|
||||
48 2 2 1 1 201 255 242
|
||||
49 2 2 1 1 167 268 219
|
||||
50 2 2 1 1 213 239 163
|
||||
51 2 2 1 1 9 180 8
|
||||
52 2 2 1 1 152 239 213
|
||||
53 2 2 1 1 169 218 80
|
||||
54 2 2 1 1 78 227 178
|
||||
55 2 2 1 1 174 252 249
|
||||
56 2 2 1 1 74 190 175
|
||||
57 2 2 1 1 251 268 247
|
||||
58 2 2 1 1 219 268 79
|
||||
59 2 2 1 1 186 201 66
|
||||
60 2 2 1 1 8 180 64
|
||||
61 2 2 1 1 79 268 251
|
||||
62 2 2 1 1 66 200 186
|
||||
63 2 2 1 1 177 190 82
|
||||
64 2 2 1 1 19 202 18
|
||||
65 2 2 1 1 77 174 172
|
||||
66 2 2 1 1 82 189 177
|
||||
67 2 2 1 1 227 272 178
|
||||
68 2 2 1 1 175 190 177
|
||||
69 2 2 1 1 265 269 166
|
||||
70 2 2 1 1 242 255 137
|
||||
71 2 2 1 1 171 265 166
|
||||
72 2 2 1 1 186 200 183
|
||||
73 2 2 1 1 189 273 54
|
||||
74 2 2 1 1 80 267 169
|
||||
75 2 2 1 1 183 200 76
|
||||
76 2 2 1 1 54 263 189
|
||||
77 2 2 1 1 74 180 9
|
||||
78 2 2 1 1 88 208 92
|
||||
79 2 2 1 1 202 203 18
|
||||
80 2 2 1 1 169 260 218
|
||||
81 2 2 1 1 201 242 202
|
||||
82 2 2 1 1 219 261 167
|
||||
83 2 2 1 1 249 252 81
|
||||
84 2 2 1 1 76 184 183
|
||||
85 2 2 1 1 62 193 179
|
||||
86 2 2 1 1 113 230 154
|
||||
87 2 2 1 1 148 270 264
|
||||
88 2 2 1 1 66 202 19
|
||||
89 2 2 1 1 92 208 207
|
||||
90 2 2 1 1 174 271 252
|
||||
91 2 2 1 1 76 196 184
|
||||
92 2 2 1 1 15 265 171
|
||||
93 2 2 1 1 264 270 182
|
||||
94 2 2 1 1 229 275 274
|
||||
95 2 2 1 1 181 264 182
|
||||
96 2 2 1 1 176 181 180
|
||||
97 2 2 1 1 7 75 6
|
||||
98 2 2 1 1 234 246 73
|
||||
99 2 2 1 1 87 208 88
|
||||
100 2 2 1 1 274 275 71
|
||||
101 2 2 1 1 193 243 194
|
||||
102 2 2 1 1 84 251 247
|
||||
103 2 2 1 1 77 271 174
|
||||
104 2 2 1 1 171 280 15
|
||||
105 2 2 1 1 51 190 50
|
||||
106 2 2 1 1 163 261 213
|
||||
107 2 2 1 1 247 257 221
|
||||
108 2 2 1 1 68 205 196
|
||||
109 2 2 1 1 180 181 64
|
||||
110 2 2 1 1 114 230 113
|
||||
111 2 2 1 1 196 205 184
|
||||
112 2 2 1 1 181 279 264
|
||||
113 2 2 1 1 50 190 74
|
||||
114 2 2 1 1 154 230 186
|
||||
115 2 2 1 1 172 266 77
|
||||
116 2 2 1 1 92 207 206
|
||||
117 2 2 1 1 62 243 193
|
||||
118 2 2 1 1 201 202 66
|
||||
119 2 2 1 1 89 223 86
|
||||
120 2 2 1 1 195 197 145
|
||||
121 2 2 1 1 140 207 161
|
||||
122 2 2 1 1 188 206 143
|
||||
123 2 2 1 1 179 193 160
|
||||
124 2 2 1 1 63 211 44
|
||||
125 2 2 1 1 202 242 203
|
||||
126 2 2 1 1 45 85 63
|
||||
127 2 2 1 1 206 207 143
|
||||
128 2 2 1 1 160 193 192
|
||||
129 2 2 1 1 4 234 73
|
||||
130 2 2 1 1 82 190 51
|
||||
131 2 2 1 1 203 242 204
|
||||
132 2 2 1 1 179 234 62
|
||||
133 2 2 1 1 131 209 187
|
||||
134 2 2 1 1 187 209 144
|
||||
135 2 2 1 1 176 279 181
|
||||
136 2 2 1 1 145 217 195
|
||||
137 2 2 1 1 143 207 140
|
||||
138 2 2 1 1 7 226 75
|
||||
139 2 2 1 1 179 246 234
|
||||
140 2 2 1 1 44 211 43
|
||||
141 2 2 1 1 199 237 206
|
||||
142 2 2 1 1 143 210 188
|
||||
143 2 2 1 1 85 223 63
|
||||
144 2 2 1 1 160 192 191
|
||||
145 2 2 1 1 186 230 201
|
||||
146 2 2 1 1 195 217 110
|
||||
147 2 2 1 1 17 203 67
|
||||
148 2 2 1 1 197 211 63
|
||||
149 2 2 1 1 6 85 45
|
||||
150 2 2 1 1 220 245 25
|
||||
151 2 2 1 1 86 223 85
|
||||
152 2 2 1 1 99 191 110
|
||||
153 2 2 1 1 197 223 145
|
||||
154 2 2 1 1 207 208 161
|
||||
155 2 2 1 1 206 237 92
|
||||
156 2 2 1 1 18 203 17
|
||||
157 2 2 1 1 63 223 197
|
||||
158 2 2 1 1 191 192 110
|
||||
159 2 2 1 1 102 191 99
|
||||
160 2 2 1 1 203 204 67
|
||||
161 2 2 1 1 156 227 215
|
||||
162 2 2 1 1 181 182 64
|
||||
163 2 2 1 1 23 196 22
|
||||
164 2 2 1 1 145 223 89
|
||||
165 2 2 1 1 192 195 110
|
||||
166 2 2 1 1 83 267 250
|
||||
167 2 2 1 1 168 257 247
|
||||
168 2 2 1 1 75 226 87
|
||||
169 2 2 1 1 193 194 192
|
||||
170 2 2 1 1 22 196 76
|
||||
171 2 2 1 1 68 196 23
|
||||
172 2 2 1 1 75 85 6
|
||||
173 2 2 1 1 159 160 133
|
||||
174 2 2 1 1 245 256 68
|
||||
175 2 2 1 1 188 210 117
|
||||
176 2 2 1 1 125 209 131
|
||||
177 2 2 1 1 157 176 175
|
||||
178 2 2 1 1 4 73 41
|
||||
179 2 2 1 1 50 74 9
|
||||
180 2 2 1 1 164 245 220
|
||||
181 2 2 1 1 159 179 160
|
||||
182 2 2 1 1 216 228 147
|
||||
183 2 2 1 1 194 243 211
|
||||
184 2 2 1 1 94 217 91
|
||||
185 2 2 1 1 40 198 81
|
||||
186 2 2 1 1 157 187 176
|
||||
187 2 2 1 1 122 210 135
|
||||
188 2 2 1 1 91 217 145
|
||||
189 2 2 1 1 135 210 143
|
||||
190 2 2 1 1 120 228 216
|
||||
191 2 2 1 1 101 199 106
|
||||
192 2 2 1 1 122 135 127
|
||||
193 2 2 1 1 133 191 102
|
||||
194 2 2 1 1 135 136 127
|
||||
195 2 2 1 1 160 191 133
|
||||
196 2 2 1 1 73 198 41
|
||||
197 2 2 1 1 140 148 136
|
||||
198 2 2 1 1 101 237 199
|
||||
199 2 2 1 1 20 200 66
|
||||
200 2 2 1 1 135 140 136
|
||||
201 2 2 1 1 178 263 78
|
||||
202 2 2 1 1 140 161 148
|
||||
203 2 2 1 1 122 127 123
|
||||
204 2 2 1 1 115 188 117
|
||||
205 2 2 1 1 106 199 115
|
||||
206 2 2 1 1 131 187 157
|
||||
207 2 2 1 1 99 110 97
|
||||
208 2 2 1 1 199 206 188
|
||||
209 2 2 1 1 5 234 4
|
||||
210 2 2 1 1 119 122 121
|
||||
211 2 2 1 1 115 199 188
|
||||
212 2 2 1 1 230 255 201
|
||||
213 2 2 1 1 76 200 21
|
||||
214 2 2 1 1 97 217 94
|
||||
215 2 2 1 1 169 267 170
|
||||
216 2 2 1 1 104 133 102
|
||||
217 2 2 1 1 56 214 78
|
||||
218 2 2 1 1 77 212 35
|
||||
219 2 2 1 1 119 210 122
|
||||
220 2 2 1 1 212 213 70
|
||||
221 2 2 1 1 17 67 16
|
||||
222 2 2 1 1 20 66 19
|
||||
223 2 2 1 1 22 76 21
|
||||
224 2 2 1 1 24 68 23
|
||||
225 2 2 1 1 69 215 214
|
||||
226 2 2 1 1 117 210 119
|
||||
227 2 2 1 1 194 197 195
|
||||
228 2 2 1 1 194 211 197
|
||||
229 2 2 1 1 215 227 214
|
||||
230 2 2 1 1 136 144 127
|
||||
231 2 2 1 1 110 217 97
|
||||
232 2 2 1 1 21 200 20
|
||||
233 2 2 1 1 36 77 35
|
||||
234 2 2 1 1 56 78 55
|
||||
235 2 2 1 1 115 117 116
|
||||
236 2 2 1 1 194 195 192
|
||||
237 2 2 1 1 41 198 40
|
||||
238 2 2 1 1 134 235 159
|
||||
239 2 2 1 1 34 212 70
|
||||
240 2 2 1 1 69 214 57
|
||||
241 2 2 1 1 208 258 161
|
||||
242 2 2 1 1 213 266 152
|
||||
243 2 2 1 1 106 115 114
|
||||
244 2 2 1 1 35 212 34
|
||||
245 2 2 1 1 57 214 56
|
||||
246 2 2 1 1 135 143 140
|
||||
247 2 2 1 1 123 209 125
|
||||
248 2 2 1 1 134 159 133
|
||||
249 2 2 1 1 26 220 25
|
||||
250 2 2 1 1 32 219 79
|
||||
251 2 2 1 1 80 218 59
|
||||
252 2 2 1 1 34 70 33
|
||||
253 2 2 1 1 58 69 57
|
||||
254 2 2 1 1 58 218 69
|
||||
255 2 2 1 1 70 219 33
|
||||
256 2 2 1 1 131 157 138
|
||||
257 2 2 1 1 147 228 128
|
||||
258 2 2 1 1 91 145 89
|
||||
259 2 2 1 1 27 221 72
|
||||
260 2 2 1 1 40 81 39
|
||||
261 2 2 1 1 52 82 51
|
||||
262 2 2 1 1 12 222 83
|
||||
263 2 2 1 1 14 71 13
|
||||
264 2 2 1 1 27 72 26
|
||||
265 2 2 1 1 71 222 13
|
||||
266 2 2 1 1 84 221 28
|
||||
267 2 2 1 1 28 221 27
|
||||
268 2 2 1 1 33 219 32
|
||||
269 2 2 1 1 59 218 58
|
||||
270 2 2 1 1 32 79 31
|
||||
271 2 2 1 1 60 80 59
|
||||
272 2 2 1 1 13 222 12
|
||||
273 2 2 1 1 12 83 11
|
||||
274 2 2 1 1 29 84 28
|
||||
275 2 2 1 1 125 131 130
|
||||
276 2 2 1 1 96 97 94
|
||||
277 2 2 1 1 166 216 147
|
||||
278 2 2 1 1 117 119 118
|
||||
279 2 2 1 1 184 185 155
|
||||
280 2 2 1 1 72 220 26
|
||||
281 2 2 1 1 157 175 158
|
||||
282 2 2 1 1 141 147 128
|
||||
283 2 2 1 1 122 123 121
|
||||
284 2 2 1 1 214 227 78
|
||||
285 2 2 1 1 165 166 147
|
||||
286 2 2 1 1 115 116 114
|
||||
287 2 2 1 1 117 118 116
|
||||
288 2 2 1 1 131 138 130
|
||||
289 2 2 1 1 98 99 97
|
||||
290 2 2 1 1 151 235 134
|
||||
291 2 2 1 1 241 283 204
|
||||
292 2 2 1 1 104 134 133
|
||||
293 2 2 1 1 111 149 146
|
||||
294 2 2 1 1 75 86 85
|
||||
295 2 2 1 1 108 224 111
|
||||
296 2 2 1 1 169 170 142
|
||||
297 2 2 1 1 172 173 151
|
||||
298 2 2 1 1 111 224 149
|
||||
299 2 2 1 1 119 121 120
|
||||
300 2 2 1 1 141 165 147
|
||||
301 2 2 1 1 54 273 53
|
||||
302 2 2 1 1 129 162 142
|
||||
303 2 2 1 1 184 205 185
|
||||
304 2 2 1 1 96 98 97
|
||||
305 2 2 1 1 112 132 109
|
||||
306 2 2 1 1 127 209 123
|
||||
307 2 2 1 1 42 62 5
|
||||
308 2 2 1 1 8 64 49
|
||||
309 2 2 1 1 45 63 44
|
||||
310 2 2 1 1 47 65 46
|
||||
311 2 2 1 1 46 226 7
|
||||
312 2 2 1 1 157 158 138
|
||||
313 2 2 1 1 48 225 47
|
||||
314 2 2 1 1 165 171 166
|
||||
315 2 2 1 1 126 128 124
|
||||
316 2 2 1 1 162 169 142
|
||||
317 2 2 1 1 125 130 126
|
||||
318 2 2 1 1 75 87 86
|
||||
319 2 2 1 1 139 163 132
|
||||
320 2 2 1 1 119 120 118
|
||||
321 2 2 1 1 149 164 146
|
||||
322 2 2 1 1 139 168 167
|
||||
323 2 2 1 1 129 141 128
|
||||
324 2 2 1 1 138 156 130
|
||||
325 2 2 1 1 123 125 124
|
||||
326 2 2 1 1 108 238 224
|
||||
327 2 2 1 1 129 142 141
|
||||
328 2 2 1 1 161 262 148
|
||||
329 2 2 1 1 68 256 205
|
||||
330 2 2 1 1 126 129 128
|
||||
331 2 2 1 1 172 174 173
|
||||
332 2 2 1 1 139 167 163
|
||||
333 2 2 1 1 125 126 124
|
||||
334 2 2 1 1 112 139 132
|
||||
335 2 2 1 1 175 177 158
|
||||
336 2 2 1 1 236 240 107
|
||||
337 2 2 1 1 93 101 95
|
||||
338 2 2 1 1 154 186 183
|
||||
339 2 2 1 1 101 106 105
|
||||
340 2 2 1 1 177 189 178
|
||||
341 2 2 1 1 106 114 113
|
||||
342 2 2 1 1 88 92 90
|
||||
343 2 2 1 1 152 172 151
|
||||
344 2 2 1 1 170 229 142
|
||||
345 2 2 1 1 88 89 86
|
||||
346 2 2 1 1 183 184 155
|
||||
347 2 2 1 1 90 91 89
|
||||
348 2 2 1 1 154 155 153
|
||||
349 2 2 1 1 113 153 105
|
||||
350 2 2 1 1 103 104 102
|
||||
351 2 2 1 1 100 108 103
|
||||
352 2 2 1 1 111 146 112
|
||||
353 2 2 1 1 100 102 99
|
||||
354 2 2 1 1 98 100 99
|
||||
355 2 2 1 1 177 178 158
|
||||
356 2 2 1 1 93 94 91
|
||||
357 2 2 1 1 111 112 109
|
||||
358 2 2 1 1 95 96 94
|
||||
359 2 2 1 1 150 152 151
|
||||
360 2 2 1 1 150 151 134
|
||||
361 2 2 1 1 118 137 116
|
||||
362 2 2 1 1 121 228 120
|
||||
363 2 2 1 1 108 111 109
|
||||
364 2 2 1 1 185 240 155
|
||||
365 2 2 1 1 96 107 98
|
||||
366 2 2 1 1 129 231 162
|
||||
367 2 2 1 1 141 229 165
|
||||
368 2 2 1 1 128 228 124
|
||||
369 2 2 1 1 153 240 236
|
||||
370 2 2 1 1 123 124 121
|
||||
371 2 2 1 1 108 109 103
|
||||
372 2 2 1 1 142 229 141
|
||||
373 2 2 1 1 87 88 86
|
||||
374 2 2 1 1 100 103 102
|
||||
375 2 2 1 1 106 113 105
|
||||
376 2 2 1 1 101 105 95
|
||||
377 2 2 1 1 154 183 155
|
||||
378 2 2 1 1 65 226 46
|
||||
379 2 2 1 1 88 90 89
|
||||
380 2 2 1 1 90 93 91
|
||||
381 2 2 1 1 113 154 153
|
||||
382 2 2 1 1 156 231 130
|
||||
383 2 2 1 1 130 231 126
|
||||
384 2 2 1 1 124 228 121
|
||||
385 2 2 1 1 93 95 94
|
||||
386 2 2 1 1 47 225 65
|
||||
387 2 2 1 1 126 231 129
|
||||
388 2 2 1 1 132 233 109
|
||||
389 2 2 1 1 144 209 127
|
||||
390 2 2 1 1 173 235 151
|
||||
391 2 2 1 1 112 232 139
|
||||
392 2 2 1 1 139 232 168
|
||||
393 2 2 1 1 104 150 134
|
||||
394 2 2 1 1 146 232 112
|
||||
395 2 2 1 1 103 233 104
|
||||
396 2 2 1 1 211 243 43
|
||||
397 2 2 1 1 109 233 103
|
||||
398 2 2 1 1 104 233 150
|
||||
399 2 2 1 1 132 239 233
|
||||
400 2 2 1 1 93 237 101
|
||||
401 2 2 1 1 92 237 90
|
||||
402 2 2 1 1 233 239 150
|
||||
403 2 2 1 1 96 236 107
|
||||
404 2 2 1 1 153 236 105
|
||||
405 2 2 1 1 95 236 96
|
||||
406 2 2 1 1 216 241 120
|
||||
407 2 2 1 1 163 239 132
|
||||
408 2 2 1 1 72 253 220
|
||||
409 2 2 1 1 162 260 169
|
||||
410 2 2 1 1 43 243 42
|
||||
411 2 2 1 1 105 236 95
|
||||
412 2 2 1 1 90 237 93
|
||||
413 2 2 1 1 100 238 108
|
||||
414 2 2 1 1 98 238 100
|
||||
415 2 2 1 1 25 245 24
|
||||
416 2 2 1 1 107 238 98
|
||||
417 2 2 1 1 187 279 176
|
||||
418 2 2 1 1 258 262 161
|
||||
419 2 2 1 1 120 241 118
|
||||
420 2 2 1 1 83 282 267
|
||||
421 2 2 1 1 150 239 152
|
||||
422 2 2 1 1 173 249 248
|
||||
423 2 2 1 1 155 240 153
|
||||
424 2 2 1 1 118 241 137
|
||||
425 2 2 1 1 221 257 72
|
||||
426 2 2 1 1 31 251 30
|
||||
427 2 2 1 1 61 250 60
|
||||
428 2 2 1 1 174 249 173
|
||||
429 2 2 1 1 173 248 235
|
||||
430 2 2 1 1 62 234 5
|
||||
431 2 2 1 1 185 244 240
|
||||
432 2 2 1 1 235 246 159
|
||||
433 2 2 1 1 220 253 164
|
||||
434 2 2 1 1 42 243 62
|
||||
435 2 2 1 1 24 245 68
|
||||
436 2 2 1 1 164 253 146
|
||||
437 2 2 1 1 238 244 224
|
||||
438 2 2 1 1 87 258 208
|
||||
439 2 2 1 1 114 255 230
|
||||
440 2 2 1 1 148 264 136
|
||||
441 2 2 1 1 69 260 215
|
||||
442 2 2 1 1 116 255 114
|
||||
443 2 2 1 1 72 257 253
|
||||
444 2 2 1 1 185 254 244
|
||||
445 2 2 1 1 198 249 81
|
||||
446 2 2 1 1 107 244 238
|
||||
447 2 2 1 1 159 246 179
|
||||
448 2 2 1 1 84 247 221
|
||||
449 2 2 1 1 240 244 107
|
||||
450 2 2 1 1 164 256 245
|
||||
451 2 2 1 1 182 259 64
|
||||
452 2 2 1 1 73 248 198
|
||||
453 2 2 1 1 235 248 246
|
||||
454 2 2 1 1 226 258 87
|
||||
455 2 2 1 1 248 249 198
|
||||
456 2 2 1 1 244 254 224
|
||||
457 2 2 1 1 55 263 54
|
||||
458 2 2 1 1 64 259 49
|
||||
459 2 2 1 1 205 254 185
|
||||
460 2 2 1 1 48 259 225
|
||||
461 2 2 1 1 60 250 80
|
||||
462 2 2 1 1 79 251 31
|
||||
463 2 2 1 1 205 256 254
|
||||
464 2 2 1 1 16 265 15
|
||||
465 2 2 1 1 77 266 212
|
||||
466 2 2 1 1 38 252 37
|
||||
467 2 2 1 1 262 270 148
|
||||
468 2 2 1 1 36 271 77
|
||||
469 2 2 1 1 146 253 232
|
||||
470 2 2 1 1 246 248 73
|
||||
471 2 2 1 1 70 261 219
|
||||
472 2 2 1 1 224 254 149
|
||||
473 2 2 1 1 137 255 116
|
||||
474 2 2 1 1 254 256 149
|
||||
475 2 2 1 1 215 281 156
|
||||
476 2 2 1 1 166 283 216
|
||||
477 2 2 1 1 11 276 1
|
||||
478 2 2 1 1 1 276 61
|
||||
479 2 2 1 1 2 277 29
|
||||
480 2 2 1 1 30 277 2
|
||||
481 2 2 1 1 259 270 225
|
||||
482 2 2 1 1 156 272 227
|
||||
483 2 2 1 1 149 256 164
|
||||
484 2 2 1 1 138 272 156
|
||||
485 2 2 1 1 178 272 158
|
||||
486 2 2 1 1 3 278 38
|
||||
487 2 2 1 1 39 278 3
|
||||
488 2 2 1 1 213 261 70
|
||||
489 2 2 1 1 229 274 165
|
||||
490 2 2 1 1 253 257 232
|
||||
491 2 2 1 1 232 257 168
|
||||
492 2 2 1 1 49 259 48
|
||||
493 2 2 1 1 65 258 226
|
||||
494 2 2 1 1 218 260 69
|
||||
495 2 2 1 1 156 281 231
|
||||
496 2 2 1 1 52 284 82
|
||||
497 2 2 1 1 252 271 37
|
||||
498 2 2 1 1 167 261 163
|
||||
499 2 2 1 1 65 262 258
|
||||
500 2 2 1 1 225 262 65
|
||||
501 2 2 1 1 189 263 178
|
||||
502 2 2 1 1 78 263 55
|
||||
503 2 2 1 1 136 264 144
|
||||
504 2 2 1 1 212 266 213
|
||||
505 2 2 1 1 67 265 16
|
||||
506 2 2 1 1 269 283 166
|
||||
507 2 2 1 1 182 270 259
|
||||
508 2 2 1 1 204 269 67
|
||||
509 2 2 1 1 152 266 172
|
||||
510 2 2 1 1 67 269 265
|
||||
511 2 2 1 1 168 268 167
|
||||
512 2 2 1 1 260 281 215
|
||||
513 2 2 1 1 15 280 14
|
||||
514 2 2 1 1 71 275 222
|
||||
515 2 2 1 1 247 268 168
|
||||
516 2 2 1 1 264 279 144
|
||||
517 2 2 1 1 37 271 36
|
||||
518 2 2 1 1 250 267 80
|
||||
519 2 2 1 1 144 279 187
|
||||
520 2 2 1 1 82 273 189
|
||||
521 2 2 1 1 225 270 262
|
||||
522 2 2 1 1 14 280 71
|
||||
523 2 2 1 1 82 284 273
|
||||
524 2 2 1 1 222 282 83
|
||||
525 2 2 1 1 158 272 138
|
||||
526 2 2 1 1 251 277 30
|
||||
527 2 2 1 1 61 276 250
|
||||
528 2 2 1 1 81 278 39
|
||||
529 2 2 1 1 83 276 11
|
||||
530 2 2 1 1 29 277 84
|
||||
531 2 2 1 1 165 274 171
|
||||
532 2 2 1 1 170 275 229
|
||||
533 2 2 1 1 53 284 10
|
||||
534 2 2 1 1 10 284 52
|
||||
535 2 2 1 1 231 281 162
|
||||
536 2 2 1 1 162 281 260
|
||||
537 2 2 1 1 250 276 83
|
||||
538 2 2 1 1 84 277 251
|
||||
539 2 2 1 1 216 283 241
|
||||
540 2 2 1 1 252 278 81
|
||||
541 2 2 1 1 38 278 252
|
||||
542 2 2 1 1 204 283 269
|
||||
543 2 2 1 1 71 280 274
|
||||
544 2 2 1 1 267 282 170
|
||||
545 2 2 1 1 170 282 275
|
||||
546 2 2 1 1 275 282 222
|
||||
547 2 2 1 1 274 280 171
|
||||
548 2 2 1 1 273 284 53
|
||||
$EndElements
|
||||
@@ -0,0 +1,926 @@
|
||||
// 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 "linear_elasticity.hpp"
|
||||
|
||||
#ifdef NVTX_DEBUG_HPP
|
||||
#undef NVTX_COLOR
|
||||
#define NVTX_COLOR ::nvtx::kCornflower
|
||||
#include NVTX_DEBUG_HPP
|
||||
#else
|
||||
#define db1(...)
|
||||
#define dbg(...)
|
||||
#endif
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
using mfem::future::dual;
|
||||
using mfem::future::tuple;
|
||||
using mfem::future::tensor;
|
||||
|
||||
using mfem::future::Weight;
|
||||
using mfem::future::Gradient;
|
||||
using mfem::future::Identity;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LinearElasticityTimeDependentOperator::LinearElasticityTimeDependentOperator(
|
||||
ParMesh &mesh_, int vorder)
|
||||
: TimeDependentOperator(),
|
||||
mesh(mesh_),
|
||||
order(vorder)
|
||||
{
|
||||
dbg();
|
||||
mesh.EnsureNodes();
|
||||
dim = mesh.Dimension();
|
||||
space_dim = mesh.SpaceDimension();
|
||||
|
||||
fec = std::make_unique<H1_FECollection>(order, dim);
|
||||
fespace = std::make_unique<ParFiniteElementSpace>(&mesh, fec.get(), dim,
|
||||
Ordering::byNODES);
|
||||
|
||||
nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
|
||||
mfes = nodes->ParFESpace();
|
||||
|
||||
domain_attributes.SetSize(mesh_.attributes.Max());
|
||||
|
||||
const mfem::FiniteElement *fe= fespace->GetFE(0);
|
||||
ir = &(IntRules.Get(fe->GetGeomType(),
|
||||
fe->GetOrder() + fe->GetOrder() + fe->GetDim() - 1));
|
||||
|
||||
qs.reset(new QuadratureSpace(mesh, *ir));
|
||||
|
||||
fqs.reset(new FaceQuadratureSpace(mesh, order+1, FaceType::Boundary));
|
||||
|
||||
ups.reset(new future::UniformParameterSpace(
|
||||
mesh, *ir, 1, false /* used_in_tensor_product */));
|
||||
|
||||
if (mesh.attributes.Size() > 0)
|
||||
{
|
||||
domain_attributes.SetSize(mesh.attributes.Max());
|
||||
domain_attributes = 1;
|
||||
}
|
||||
|
||||
//set the block sizes for the solution, rhs and tmp vectors
|
||||
block_true_offsets.SetSize(3);
|
||||
block_true_offsets[0] = 0;
|
||||
block_true_offsets[1] = fespace->TrueVSize();
|
||||
block_true_offsets[2] = fespace->TrueVSize();
|
||||
|
||||
block_true_offsets.PartialSum();
|
||||
|
||||
sol.Update(block_true_offsets); sol=0.0; sol.UseDevice(true);
|
||||
rhs.Update(block_true_offsets); rhs=0.0; rhs.UseDevice(true);
|
||||
tmp.Update(block_true_offsets); tmp=0.0; tmp.UseDevice(true);
|
||||
|
||||
res.SetSize(fespace->GetTrueVSize()); res=0.0; res.UseDevice(true);
|
||||
|
||||
displ.SetSpace(fespace.get()); displ=0.0;
|
||||
displ.SetTrueVector();
|
||||
displ.GetTrueVector().UseDevice(true);
|
||||
|
||||
veloc.SetSpace(fespace.get()); veloc=0.0;
|
||||
veloc.SetTrueVector();
|
||||
veloc.GetTrueVector().UseDevice(true);
|
||||
|
||||
this->width = 2*fespace->TrueVSize();
|
||||
this->height = 2*fespace->TrueVSize();
|
||||
|
||||
MPI_Comm_rank(mesh.GetComm(),&myrank);
|
||||
|
||||
vol_force_mem.SetSize(10);
|
||||
vol_force_mem.UseDevice(true);
|
||||
vol_force_mem(0) = 0.0; // time
|
||||
vol_force_mem(1) = 1.0; // period
|
||||
vol_force_mem(2) = 0.0; // amplitude
|
||||
vol_force_mem(3) = 0.5; // radius
|
||||
vol_force_mem(4) = 0.0; // x coordinate of the center
|
||||
vol_force_mem(5) = 0.0; // y coordinate of the center
|
||||
vol_force_mem(6) = 0.0; // z coordinate of the center
|
||||
vol_force_mem(7) = 5*vol_force_mem(1); // total train length
|
||||
vol_force_mem(8) = vol_force_mem(7)/2.0;
|
||||
vol_force_mem(9) = 2.0;
|
||||
|
||||
bdr_force_mem.SetSize(3);
|
||||
bdr_force_mem.UseDevice(true);
|
||||
bdr_force_mem(0) = 0.0; // time
|
||||
bdr_force_mem(1) = 1.0; // period
|
||||
bdr_force_mem(2) = 0.0; // amplitude
|
||||
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LinearElasticityTimeDependentOperator::SetObjective(
|
||||
std::shared_ptr<Operator> op_)
|
||||
{
|
||||
dbg();
|
||||
if (op_.get() != nullptr)
|
||||
{
|
||||
obj = op_;
|
||||
dbg("set the new objective and readjust the size of the operator and the state");
|
||||
block_true_offsets.SetSize(4);
|
||||
block_true_offsets[0] = 0;
|
||||
block_true_offsets[1] = fespace->TrueVSize();
|
||||
block_true_offsets[2] = fespace->TrueVSize();
|
||||
block_true_offsets[3] = op_->Height();
|
||||
|
||||
block_true_offsets.PartialSum();
|
||||
|
||||
sol.Update(block_true_offsets); sol=0.0; sol.UseDevice(true);
|
||||
rhs.Update(block_true_offsets); rhs=0.0; rhs.UseDevice(true);
|
||||
tmp.Update(block_true_offsets); tmp=0.0; tmp.UseDevice(true);
|
||||
|
||||
this->width = block_true_offsets[3];
|
||||
this->height = block_true_offsets[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg("New Objective");
|
||||
obj.reset();
|
||||
|
||||
block_true_offsets.SetSize(3);
|
||||
block_true_offsets[0] = 0;
|
||||
block_true_offsets[1] = fespace->TrueVSize();
|
||||
block_true_offsets[2] = fespace->TrueVSize();
|
||||
block_true_offsets.PartialSum();
|
||||
|
||||
sol.Update(block_true_offsets); sol.UseDevice(true); sol = 0.0;
|
||||
rhs.Update(block_true_offsets); rhs.UseDevice(true); rhs = 0.0;
|
||||
tmp.Update(block_true_offsets); tmp.UseDevice(true); tmp = 0.0;
|
||||
|
||||
this->width = 2*fespace->TrueVSize();
|
||||
this->height = 2*fespace->TrueVSize();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DI, typename scalar_t=real_t> struct QElasticityFunction
|
||||
{
|
||||
using vec_t = tensor<real_t, DI>;
|
||||
using vecd_t = tensor<scalar_t, DI>;
|
||||
|
||||
using mat_t = tensor<real_t, DI, DI>;
|
||||
using matd_t = tensor<scalar_t, DI, DI>;
|
||||
|
||||
struct Mass
|
||||
{
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const real_t &dens1,
|
||||
const real_t &dens2,
|
||||
const scalar_t &density,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
const auto dens = density*dens2 + (1.0-density)*dens1;
|
||||
const auto detJ = mfem::future::det(J);
|
||||
return tuple{dens * u * detJ * w};
|
||||
}
|
||||
};
|
||||
|
||||
struct Elasticity
|
||||
{
|
||||
MFEM_HOST_DEVICE inline auto operator()(const matd_t &dudxi,
|
||||
const real_t &L1,
|
||||
const real_t &M1,
|
||||
const real_t &L2,
|
||||
const real_t &M2,
|
||||
const scalar_t &density,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
const matd_t JxW = transpose(inv(J)) * det(J) * w;
|
||||
constexpr auto I = mfem::future::IsotropicIdentity<DI>();
|
||||
const auto eps = mfem::future::sym(dudxi * mfem::future::inv(J));
|
||||
const auto L = density*L2 + (1.0-density)*L1;
|
||||
const auto M = density*M2 + (1.0-density)*M1;
|
||||
return tuple{(L * tr(eps) * I + 2.0 * M * eps) * JxW};
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicBdrForce
|
||||
{
|
||||
// real_t time=0.0;
|
||||
// real_t period=1.0;
|
||||
// mfem::Vector* time_mem;
|
||||
const real_t* time_mem;
|
||||
|
||||
//mfem::Memory<int> alt_time; check the documentation about Memory class for more details
|
||||
|
||||
DynamicBdrForce(mfem::Vector &tm)
|
||||
// the Read method should be called on the vector passed as tm
|
||||
// before calling the Mult on the differentiable operator when
|
||||
// the time is changing, i.e., the values between the host
|
||||
// and device have to be synchronized.
|
||||
{
|
||||
time_mem = tm.Read(); //get the device pointer
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
const real_t time = time_mem[0]; // (*time_mem)(0);
|
||||
const real_t period = time_mem[1]; // (*time_mem)(1);
|
||||
const real_t amplitude = time_mem[2]; // (*time_mem)(2);
|
||||
const auto detJ = mfem::future::det(J);
|
||||
// time dependent force in x direction
|
||||
const real_t force_amplitude = (time > 0.0) ? amplitude*sin(
|
||||
M_PI*time/period) : 0.0;
|
||||
vecd_t force {0};//= vecd_t::Zero();
|
||||
force(0) = force_amplitude;
|
||||
return tuple{force * detJ * w};
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicVolForce
|
||||
{
|
||||
const real_t* time_mem;
|
||||
DynamicVolForce(mfem::Vector& tm)
|
||||
{
|
||||
time_mem = tm.Read(); //get the device pointer
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const vec_t &x,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
|
||||
{
|
||||
const real_t time = *(time_mem+0);
|
||||
const real_t period = *(time_mem+1);
|
||||
const real_t amplitude = *(time_mem+2);
|
||||
const real_t radius = *(time_mem+3);
|
||||
|
||||
const real_t L=*(time_mem+7);
|
||||
const real_t t0=*(time_mem+8);
|
||||
const real_t n=*(time_mem+9);
|
||||
|
||||
const real_t envelope_ampl= (time< L) ?pow(cos(M_PI*(time-t0)/L),n) : 0.0;
|
||||
const real_t force_amplitude = (time > 0.0) ? amplitude*sin(
|
||||
2.0*M_PI*time/period) : 0.0;
|
||||
|
||||
vecd_t force {0};
|
||||
|
||||
// time dependent force in x direction
|
||||
force(0) = force_amplitude*envelope_ampl;
|
||||
|
||||
//compute the distance from the center of the force application
|
||||
real_t dist_sq = 0.0;
|
||||
for (int i = 0; i < DI; i++)
|
||||
{
|
||||
const real_t diff = x(i) - *(time_mem+4+i);
|
||||
dist_sq += diff * diff;
|
||||
}
|
||||
|
||||
// apply the force only within the specified radius
|
||||
if (dist_sq > radius*radius)
|
||||
{
|
||||
force(0) = 0.0;
|
||||
}
|
||||
|
||||
const auto detJ = mfem::future::det(J);
|
||||
return tuple{force * detJ * w};
|
||||
}
|
||||
|
||||
struct Objective
|
||||
{
|
||||
/* data */
|
||||
const real_t* obj_mem;
|
||||
Objective(mfem::Vector& tm)
|
||||
{
|
||||
obj_mem = tm.Read(); // get the device pointer
|
||||
}
|
||||
|
||||
// takes velocity and returns squared velocity
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const vec_t &x,
|
||||
const mat_t &J,
|
||||
const real_t &w
|
||||
) const
|
||||
{
|
||||
// const real_t time = *(obj_mem+0);
|
||||
const real_t radius = *(obj_mem+1);
|
||||
//compute the distance from the center of the objective circle/sphere
|
||||
real_t dist_sq = 0.0;
|
||||
scalar_t obj = 0.0;
|
||||
|
||||
for (int i = 0; i < DI; i++)
|
||||
{
|
||||
const real_t diff = x(i) - *(obj_mem+2+i);
|
||||
dist_sq += diff * diff;
|
||||
obj += u(i) * u(i);
|
||||
}
|
||||
|
||||
// apply the obj only within the specified radius
|
||||
if (dist_sq > radius*radius)
|
||||
{
|
||||
obj = 0.0;
|
||||
}
|
||||
|
||||
const auto detJ = mfem::future::det(J);
|
||||
return tuple{obj * detJ * w};
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjectiveGrad
|
||||
{
|
||||
/* data */
|
||||
const real_t* obj_mem;
|
||||
|
||||
ObjectiveGrad(mfem::Vector& tm)
|
||||
{
|
||||
obj_mem = tm.Read(); // get the device pointer
|
||||
}
|
||||
|
||||
// takes velocity and returns squared velocity
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const vecd_t &x,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
// const real_t time = *(obj_mem+0);
|
||||
const real_t radius = *(obj_mem+1);
|
||||
//compute the distance from the center of the objective circle/sphere
|
||||
scalar_t dist_sq = 0.0;
|
||||
vecd_t obj_grad;
|
||||
real_t objc = 1.0;
|
||||
|
||||
for (int i = 0; i < DI; i++)
|
||||
{
|
||||
const real_t diff = x(i) - *(obj_mem+2+i);
|
||||
dist_sq += diff * diff;
|
||||
|
||||
obj_grad(i) = 2.0 * u(i);
|
||||
}
|
||||
|
||||
// apply the obj only within the specified radius
|
||||
if (dist_sq > radius*radius)
|
||||
{
|
||||
objc = 0.0;
|
||||
}
|
||||
|
||||
const auto detJ = mfem::future::det(J);
|
||||
return tuple{objc* obj_grad * detJ * w};
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class InterpolatedCoefficient : public mfem::Coefficient
|
||||
{
|
||||
public:
|
||||
InterpolatedCoefficient(mfem::Coefficient &c1, mfem::Coefficient &c2,
|
||||
mfem::Coefficient &c3)
|
||||
: coeff1(c1), coeff2(c2), coeff3(c3) {}
|
||||
|
||||
double Eval(mfem::ElementTransformation &T,
|
||||
const mfem::IntegrationPoint &ip) override
|
||||
{
|
||||
real_t c1=coeff1.Eval(T, ip);
|
||||
real_t c2=coeff2.Eval(T, ip);
|
||||
real_t dens=coeff3.Eval(T, ip);
|
||||
return c2*dens + (1.0-dens)*c1;
|
||||
}
|
||||
|
||||
private:
|
||||
mfem::Coefficient &coeff1, &coeff2, &coeff3;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LinearElasticityTimeDependentOperator::AssembleExplicit()
|
||||
{
|
||||
dbg();
|
||||
// define the mass differentiable operator
|
||||
{
|
||||
dfem_mass_op = std::make_unique<mfem::future::DifferentiableOperator>(
|
||||
std::vector<mfem::future::FieldDescriptor> { {FDispl, fespace.get()} },
|
||||
std::vector<mfem::future::FieldDescriptor>
|
||||
{
|
||||
{Dens1, ups.get()},
|
||||
{Dens2, ups.get()},
|
||||
{Density, ups.get()},
|
||||
{Coords, mfes}
|
||||
},
|
||||
mesh);
|
||||
|
||||
dfem_mass_op->SetParameters({ dens1.get(), dens2.get(), density.get(), nodes });
|
||||
|
||||
const auto minputs =
|
||||
mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FDispl>{},
|
||||
mfem::future::Identity<Dens1>{},
|
||||
mfem::future::Identity<Dens2>{},
|
||||
mfem::future::Identity<Density>{},
|
||||
mfem::future::Gradient<Coords>{},
|
||||
mfem::future::Weight{}
|
||||
};
|
||||
|
||||
const auto moutputs = mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FDispl>{}
|
||||
};
|
||||
|
||||
if (2 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<2>::Mass mass_func;
|
||||
dfem_mass_op->AddDomainIntegrator(mass_func, minputs, moutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
else if (3 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<3>::Mass mass_func;
|
||||
dfem_mass_op->AddDomainIntegrator(mass_func, minputs, moutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
// define the damp differentiable operator
|
||||
{
|
||||
dfem_damp_op = std::make_unique<mfem::future::DifferentiableOperator>(
|
||||
std::vector<mfem::future::FieldDescriptor> { {FVeloc, fespace.get()} },
|
||||
std::vector<mfem::future::FieldDescriptor>
|
||||
{
|
||||
{CMass1, ups.get()},
|
||||
{CMass2, ups.get()},
|
||||
{Density, ups.get()},
|
||||
{Coords, mfes}
|
||||
},
|
||||
mesh);
|
||||
|
||||
dfem_damp_op->SetParameters({ cm1.get(), cm2.get(), density.get(), nodes });
|
||||
|
||||
const auto dinputs =
|
||||
mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FVeloc>{},
|
||||
mfem::future::Identity<CMass1>{},
|
||||
mfem::future::Identity<CMass2>{},
|
||||
mfem::future::Identity<Density>{},
|
||||
mfem::future::Gradient<Coords>{},
|
||||
mfem::future::Weight{}
|
||||
};
|
||||
|
||||
const auto doutputs = mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FVeloc>{}
|
||||
};
|
||||
|
||||
if (2 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<2>::Mass damp_func;
|
||||
dfem_damp_op->AddDomainIntegrator(damp_func, dinputs, doutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
else if (3 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<3>::Mass damp_func;
|
||||
dfem_damp_op->AddDomainIntegrator(damp_func, dinputs, doutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
// define the volumetric force differentiable operator
|
||||
{
|
||||
dfem_vol_force_op = std::make_unique<mfem::future::DifferentiableOperator>(
|
||||
std::vector<mfem::future::FieldDescriptor> { {FDispl, fespace.get()} },
|
||||
std::vector<mfem::future::FieldDescriptor>
|
||||
{
|
||||
{Coords, mfes}
|
||||
},
|
||||
mesh);
|
||||
|
||||
dfem_vol_force_op->SetParameters({ nodes });
|
||||
|
||||
const auto finputs =
|
||||
mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FDispl>{},
|
||||
mfem::future::Value<Coords>{},
|
||||
mfem::future::Gradient<Coords>{},
|
||||
mfem::future::Weight{}
|
||||
};
|
||||
|
||||
const auto foutputs =
|
||||
mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FDispl>{}
|
||||
};
|
||||
|
||||
if (2 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<2>::DynamicVolForce vol_force_func(vol_force_mem);
|
||||
dfem_vol_force_op->AddDomainIntegrator(vol_force_func, finputs, foutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
else if (3 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<3>::DynamicVolForce vol_force_func(vol_force_mem);
|
||||
dfem_vol_force_op->AddDomainIntegrator(vol_force_func, finputs, foutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
// define the linear elasticity differentiable operator
|
||||
{
|
||||
dfem_forward_op = std::make_unique<mfem::future::DifferentiableOperator>(
|
||||
std::vector<mfem::future::FieldDescriptor> { {FDispl, fespace.get()} },
|
||||
std::vector<mfem::future::FieldDescriptor>
|
||||
{
|
||||
{Lambda1, ups.get()},
|
||||
{Mu1, ups.get()},
|
||||
{Lambda2, ups.get()},
|
||||
{Mu2, ups.get()},
|
||||
{Density, ups.get()},
|
||||
{Coords, mfes}
|
||||
},
|
||||
mesh);
|
||||
|
||||
dfem_forward_op->SetParameters({ l1.get(), m1.get(), l2.get(), m2.get(), density.get(), nodes });
|
||||
|
||||
const auto finputs = mfem::future::tuple
|
||||
{
|
||||
mfem::future::Gradient<FDispl>{},
|
||||
mfem::future::Identity<Lambda1>{},
|
||||
mfem::future::Identity<Mu1>{},
|
||||
mfem::future::Identity<Lambda2>{},
|
||||
mfem::future::Identity<Mu2>{},
|
||||
mfem::future::Identity<Density>{},
|
||||
mfem::future::Gradient<Coords>{},
|
||||
mfem::future::Weight{}
|
||||
};
|
||||
|
||||
const auto foutputs = mfem::future::tuple
|
||||
{
|
||||
mfem::future::Gradient<FDispl>{}
|
||||
};
|
||||
|
||||
if (2 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<2>::Elasticity elasticity_func;
|
||||
dfem_forward_op->AddDomainIntegrator(elasticity_func, finputs, foutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
else if (3 == space_dim)
|
||||
{
|
||||
typename QElasticityFunction<3>::Elasticity elasticity_func;
|
||||
dfem_forward_op->AddDomainIntegrator(elasticity_func, finputs, foutputs, *ir,
|
||||
domain_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
// Spectral mass-matrix
|
||||
{
|
||||
InterpolatedCoefficient interp_dens1(*cdens1, *cdens2, *cdensity);
|
||||
IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
|
||||
const IntegrationRule &ir_ni = gll_rules.Get(mesh.GetTypicalElementGeometry(),
|
||||
2 * order - 1);
|
||||
|
||||
ParBilinearForm bf_lor(fespace.get());
|
||||
auto *mv_blfi = new VectorMassIntegrator(interp_dens1);
|
||||
mv_blfi->SetIntRule(&ir_ni);
|
||||
//bf_lor.AddDomainIntegrator(new LumpedIntegrator(mv_blfi));
|
||||
bf_lor.AddDomainIntegrator(mv_blfi);
|
||||
bf_lor.Assemble();
|
||||
bf_lor.Finalize();
|
||||
M_lor.reset(bf_lor.ParallelAssemble());
|
||||
}
|
||||
|
||||
// allocate the AMG preconditioner and CG solver
|
||||
// for the mass matrix
|
||||
{
|
||||
amg = std::make_unique<HypreBoomerAMG>();
|
||||
amg->SetPrintLevel(1);
|
||||
amg->SetOperator(*M_lor);
|
||||
|
||||
cg = std::make_unique<CGSolver>(mesh.GetComm());
|
||||
cg->SetRelTol(1e-7);
|
||||
cg->SetAbsTol(1e-12);
|
||||
cg->SetMaxIter(500);
|
||||
cg->SetPrintLevel(0);
|
||||
cg->SetOperator(*dfem_mass_op);
|
||||
//cg->SetOperator(*M_lor);
|
||||
cg->SetPreconditioner(*amg);
|
||||
cg->iterative_mode=false;
|
||||
}
|
||||
|
||||
//set the zero bdr conditions
|
||||
{
|
||||
Array<int> bdr_attr; bdr_attr.SetSize(mesh.bdr_attributes.Max());
|
||||
bdr_attr=0;
|
||||
for (const auto &it:zero_bdrs)
|
||||
{
|
||||
bdr_attr[it-1]=1.0;
|
||||
}
|
||||
fespace->GetEssentialTrueDofs(bdr_attr,ess_tdof_list);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LinearElasticityTimeDependentOperator::Mult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
db1();
|
||||
real_t time = this->GetTime();
|
||||
|
||||
BlockVector bx(const_cast<Vector&>(x), block_true_offsets);
|
||||
BlockVector by(y, block_true_offsets);
|
||||
|
||||
displ.GetTrueVector().Set(1.0,bx.GetBlock(0));
|
||||
veloc.GetTrueVector().Set(1.0,bx.GetBlock(1));
|
||||
//set zero BC
|
||||
{
|
||||
int N = ess_tdof_list.Size();
|
||||
real_t *dp=displ.GetTrueVector().ReadWrite();
|
||||
real_t *vp=veloc.GetTrueVector().ReadWrite();
|
||||
const int *ep = ess_tdof_list.Read();
|
||||
mfem::forall(N, [=] MFEM_HOST_DEVICE(int i)
|
||||
{
|
||||
dp[ep[i]] = 0.0;
|
||||
vp[ep[i]] = 0.0;
|
||||
});
|
||||
}
|
||||
//displ.SetFromTrueVector();
|
||||
//veloc.SetFromTrueVector();
|
||||
|
||||
by.GetBlock(0).Set(1.0, veloc.GetTrueVector()); // dx/dt = velocity
|
||||
|
||||
// compute the residual
|
||||
|
||||
// 1) add external volumetric forces
|
||||
real_t* pvol_force_mem=vol_force_mem.HostReadWrite(); //get the host pointer
|
||||
pvol_force_mem[0]=time; //set the current time to be pass to the integrator
|
||||
vol_force_mem.Read(); //copy force_mem from host to device
|
||||
// call the kernel computing f_ext
|
||||
// dfem_vol_force_op->SetParameters({nodes}); // it is already set
|
||||
dfem_vol_force_op->Mult(veloc.GetTrueVector(),res);
|
||||
|
||||
// 2) compute the mass proportional viscous damping term
|
||||
// dfem_damp_op->SetParameters({ cm1.get(), cm2.get(), density.get(), nodes });
|
||||
dfem_damp_op->Mult(veloc.GetTrueVector(), tmp.GetBlock(1));
|
||||
res -= tmp.GetBlock(1);
|
||||
|
||||
// 3) add the stiffness proportional viscous damping term
|
||||
|
||||
// 4) add the elastic force term
|
||||
dfem_forward_op->Mult(displ.GetTrueVector(),tmp.GetBlock(0));
|
||||
res-= tmp.GetBlock(0);
|
||||
|
||||
|
||||
//dfem_mass_op->SetParameters({dens1.get(), dens2.get(), density.get(), nodes});
|
||||
cg->Mult(res, by.GetBlock(1)); // solve for acceleration
|
||||
|
||||
//check if objective is valid
|
||||
if (obj.get()!=nullptr)
|
||||
{
|
||||
//evaluate the objective contribution
|
||||
obj->Mult(x,by.GetBlock(2));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// implements the adjoint reverse time integration
|
||||
// i.e. x=[l_q,l_v, L_\rho]^T y=x' - i.e. the derivative with respect to \tau=T-t
|
||||
// before calling MultTranspose one should set the sol vector with the
|
||||
// solution for the forward problem at time t
|
||||
void LinearElasticityTimeDependentOperator::AdjointMult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
dbg();
|
||||
BlockVector bx(const_cast<Vector&>(x), block_true_offsets);
|
||||
BlockVector by(y, block_true_offsets);
|
||||
|
||||
y=0.0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void LinearElasticityTimeDependentOperator::ImplicitSolve(
|
||||
const real_t dt,
|
||||
const Vector &x,
|
||||
Vector &k)
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DI, typename scalar_t=real_t> struct QObjectiveFunction
|
||||
{
|
||||
using matd_t = tensor<scalar_t, DI, DI>;
|
||||
using vecd_t = tensor<scalar_t, DI>;
|
||||
using vec_t = tensor<real_t, DI>;
|
||||
using mat_t = tensor<real_t, DI, DI>;
|
||||
|
||||
struct Objective1
|
||||
{
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const real_t &co,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
scalar_t rez {0.0};
|
||||
for (int i=0; i<DI; i++) { rez = rez + u(i)*u(i); }
|
||||
rez = rez*co;
|
||||
const auto detJ = mfem::future::det(J);
|
||||
return tuple{rez * detJ * w};
|
||||
}
|
||||
};
|
||||
|
||||
struct Objective2
|
||||
{
|
||||
const real_t s1, s2;
|
||||
Objective2(real_t s1 = 1.0, real_t s2 = 1.0): s1(s1), s2(s2) { }
|
||||
|
||||
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
|
||||
const vecd_t &v,
|
||||
const real_t &co,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
scalar_t rez = 0.0;
|
||||
for (int i=0; i<DI; i++)
|
||||
{
|
||||
rez = rez + u(i)*u(i)*s1 + v(i)*v(i)*s2;
|
||||
}
|
||||
const auto detJ = mfem::future::det(J);
|
||||
return tuple{rez * detJ * w};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
ExampleObjectiveIntegrand::ExampleObjectiveIntegrand(ParFiniteElementSpace*
|
||||
fes_,
|
||||
std::shared_ptr<mfem::Coefficient> objc_)
|
||||
{
|
||||
dbg();
|
||||
fes=fes_;
|
||||
fes->GetParMesh()->EnsureNodes();
|
||||
|
||||
disp.SetSpace(fes); disp=0.0;
|
||||
velo.SetSpace(fes); velo=0.0;
|
||||
|
||||
this->width=2*fes->GetTrueVSize(); //disp.Size() + veloc.Size()
|
||||
this->height=1; //returns 3 objectives
|
||||
|
||||
grad=nullptr;
|
||||
|
||||
//set the block sizes for the solution, rhs and tmp vectors
|
||||
block_true_offsets.SetSize(3);
|
||||
block_true_offsets[0] = 0;
|
||||
block_true_offsets[1] = fes->TrueVSize();
|
||||
block_true_offsets[2] = fes->TrueVSize();
|
||||
|
||||
block_true_offsets.PartialSum();
|
||||
|
||||
const mfem::FiniteElement *fe= fes->GetFE(0);
|
||||
ir = &(IntRules.Get(fe->GetGeomType(),
|
||||
fe->GetOrder() + fe->GetOrder() + fe->GetDim() - 1));
|
||||
|
||||
qs.reset(new QuadratureSpace(*(fes->GetParMesh()), *ir));
|
||||
|
||||
ups.reset(new future::UniformParameterSpace(
|
||||
*(fes->GetParMesh()), *ir, 1, false /* used_in_tensor_product */));
|
||||
|
||||
if (fes->GetParMesh()->attributes.Size() > 0)
|
||||
{
|
||||
domain_attributes.SetSize(fes->GetParMesh()->attributes.Max());
|
||||
domain_attributes = 1;
|
||||
}
|
||||
|
||||
nodes = static_cast<ParGridFunction *>(fes->GetParMesh()->GetNodes());
|
||||
mfes = nodes->ParFESpace();
|
||||
|
||||
SetCoefficients(objc_);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void ExampleObjectiveIntegrand::SetCoefficients(
|
||||
std::shared_ptr<mfem::Coefficient> objc)
|
||||
{
|
||||
dbg();
|
||||
co=objc;
|
||||
if (co.get()!=nullptr)
|
||||
{
|
||||
//project the coefficient
|
||||
density.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
density->Project(*co);
|
||||
}
|
||||
else
|
||||
{
|
||||
density.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
density->SetConstant(1.0);
|
||||
}
|
||||
|
||||
res.SetSize(density->Size());
|
||||
|
||||
//allocate the differentiable operator
|
||||
{
|
||||
obj = std::make_unique<mfem::future::DifferentiableOperator>(
|
||||
std::vector<mfem::future::FieldDescriptor> { {FDispl, fes} },
|
||||
std::vector<mfem::future::FieldDescriptor>
|
||||
{
|
||||
{Density, ups.get()},
|
||||
{Coords, mfes}
|
||||
},
|
||||
*(fes->GetParMesh())
|
||||
);
|
||||
|
||||
obj->SetParameters( {density.get(), nodes} );
|
||||
|
||||
const auto finputs =
|
||||
mfem::future::tuple
|
||||
{
|
||||
mfem::future::Value<FDispl>{},
|
||||
mfem::future::Identity<Density>{},
|
||||
mfem::future::Gradient<Coords>{},
|
||||
mfem::future::Weight{}
|
||||
};
|
||||
|
||||
const auto foutputs =
|
||||
mfem::future::tuple
|
||||
{
|
||||
mfem::future::Identity<Density>{}
|
||||
};
|
||||
|
||||
int space_dim=fes->GetParMesh()->SpaceDimension();
|
||||
|
||||
if (2 == space_dim)
|
||||
{
|
||||
using mfem::future::dual;
|
||||
using dual_t = dual<real_t, real_t>;
|
||||
typename QObjectiveFunction<2,dual_t>::Objective1 obj_func;
|
||||
auto derivatives = std::integer_sequence<size_t, FDispl, Coords> {};
|
||||
obj->AddDomainIntegrator(obj_func, finputs, foutputs, *ir, domain_attributes,
|
||||
derivatives);
|
||||
}
|
||||
else if ( 3 == space_dim)
|
||||
{
|
||||
using mfem::future::dual;
|
||||
using dual_t = dual<real_t, real_t>;
|
||||
typename QObjectiveFunction<3,dual_t>::Objective1 obj_func;
|
||||
auto derivatives = std::integer_sequence<size_t, FDispl, Coords> {};
|
||||
obj->AddDomainIntegrator(obj_func, finputs, foutputs, *ir, domain_attributes,
|
||||
derivatives);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void ExampleObjectiveIntegrand::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
db1();
|
||||
mfem::Array<int> lblock_true_offsets;
|
||||
lblock_true_offsets.SetSize(4);
|
||||
lblock_true_offsets[0] = 0;
|
||||
lblock_true_offsets[1] = fes->TrueVSize();
|
||||
lblock_true_offsets[2] = fes->TrueVSize();
|
||||
lblock_true_offsets[3] = x.Size()-2*fes->TrueVSize();
|
||||
lblock_true_offsets.PartialSum();
|
||||
|
||||
BlockVector bx(const_cast<Vector&>(x), lblock_true_offsets);
|
||||
|
||||
obj->Mult(bx.GetBlock(0),res);
|
||||
// sum up the weighted values
|
||||
real_t lp = mfem::InnerProduct(fes->GetComm(), res, *density);
|
||||
y[0]=lp;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void ExampleObjectiveIntegrand::EvalGradient(const Vector &x,
|
||||
Vector &grad_y) const
|
||||
{
|
||||
dbg();
|
||||
mfem::Array<int> lblock_true_offsets;
|
||||
lblock_true_offsets.SetSize(4);
|
||||
lblock_true_offsets[0] = 0;
|
||||
lblock_true_offsets[1] = fes->TrueVSize();
|
||||
lblock_true_offsets[2] = fes->TrueVSize();
|
||||
lblock_true_offsets[3] = x.Size()-2*fes->TrueVSize();
|
||||
lblock_true_offsets.PartialSum();
|
||||
|
||||
BlockVector bx(const_cast<Vector&>(x), lblock_true_offsets);
|
||||
BlockVector by(grad_y, lblock_true_offsets); by=0.0;
|
||||
disp.SetFromTrueDofs(bx.GetBlock(0));
|
||||
|
||||
std::shared_ptr<mfem::future::DerivativeOperator> dobj_du;
|
||||
dobj_du=obj->GetDerivative(FDispl, {&disp}, {density.get(), nodes});
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
std::cout << "Op size: " << dobj_du->Height()<<" "<<dobj_du->Width()<<
|
||||
std::endl;
|
||||
std::cout << " disp size:"<< bx.GetBlock(0).Size()<<std::endl;
|
||||
std::cout << " dens size:"<< density->Size()<<std::endl;
|
||||
}
|
||||
|
||||
dobj_du->MultTranspose(*density,by.GetBlock(0));
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
// 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 LINEAR_ELASTICITY_HPP
|
||||
#define LINEAR_ELASTICITY_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class LinearElasticityTimeDependentOperator: public mfem::TimeDependentOperator
|
||||
{
|
||||
public:
|
||||
|
||||
LinearElasticityTimeDependentOperator(mfem::ParMesh &mesh_, int vorder =1);
|
||||
|
||||
virtual ~LinearElasticityTimeDependentOperator() { }
|
||||
|
||||
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
|
||||
|
||||
virtual void AdjointMult(const mfem::Vector &x, mfem::Vector &y) const;
|
||||
|
||||
void ImplicitSolve(const mfem::real_t dt,
|
||||
const mfem::Vector &x,
|
||||
mfem::Vector &k) override;
|
||||
|
||||
// sets the objective integraand which will be integrated with the state of the system
|
||||
void SetObjective(std::shared_ptr<mfem::Operator> op_);
|
||||
|
||||
// Assemble the explicit operators
|
||||
// must be called after setting all material coefficients
|
||||
// and before time stepping
|
||||
void AssembleExplicit();
|
||||
|
||||
void SetDensity(mfem::Coefficient &rho)
|
||||
{
|
||||
density = std::make_shared<mfem::CoefficientVector>(*qs,
|
||||
mfem::CoefficientStorage::FULL);
|
||||
cdensity = ρ
|
||||
density->Project(rho);
|
||||
}
|
||||
|
||||
// l1, m1 are the Lame parameters for material 1
|
||||
// l2, m2 are the Lame parameters for material 2
|
||||
void SetElasticityCoefficients(mfem::Coefficient& l1_,
|
||||
mfem::Coefficient& m1_,
|
||||
mfem::Coefficient& l2_,
|
||||
mfem::Coefficient& m2_)
|
||||
{
|
||||
l1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
l2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
m1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
m2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
|
||||
l1->Project(l1_);
|
||||
m1->Project(m1_);
|
||||
l2->Project(l2_);
|
||||
m2->Project(m2_);
|
||||
}
|
||||
|
||||
// rho1 and rho2 are the density coefficients for material 1 and 2
|
||||
void SetDensityMaterialCoefficients(mfem::Coefficient& rho1_,
|
||||
mfem::Coefficient& rho2_)
|
||||
{
|
||||
dens1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
dens2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
|
||||
cdens1 = &rho1_;
|
||||
cdens2 = &rho2_;
|
||||
|
||||
dens1->Project(rho1_);
|
||||
dens2->Project(rho2_);
|
||||
}
|
||||
|
||||
// c1 and c2 are velocity proportional damping coefficients for material 1 and 2
|
||||
// both of them are considered to be time dependent
|
||||
void SetDampingMaterialCoefficients(mfem::Coefficient& c1_,
|
||||
mfem::Coefficient& c2_)
|
||||
{
|
||||
cm1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
cm2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
|
||||
cm1->Project(c1_);
|
||||
cm2->Project(c2_);
|
||||
}
|
||||
|
||||
// dl1, dm1 are the strain velocity proportional damping coefficients
|
||||
// for material 1 and dl2, dm2 for material 2
|
||||
// all of them are considered to be time dependent
|
||||
void SetDampingMaterialCoefficients(mfem::Coefficient& dl1_,
|
||||
mfem::Coefficient& dm1_,
|
||||
mfem::Coefficient& dl2_,
|
||||
mfem::Coefficient& dm2_)
|
||||
{
|
||||
dl1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
dl2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
dm1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
dm2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
|
||||
|
||||
dl1->Project(dl1_);
|
||||
dm1->Project(dm1_);
|
||||
dl2->Project(dl2_);
|
||||
dm2->Project(dm2_);
|
||||
}
|
||||
|
||||
mfem::ParGridFunction& GetDisplacement() { return displ; }
|
||||
mfem::ParGridFunction& GetVelocity() { return veloc; }
|
||||
mfem::Array<int>& GetTrueBlockOffsets() { return block_true_offsets;}
|
||||
|
||||
mfem::Vector& GetState() {return sol;}
|
||||
|
||||
const mfem::ParFiniteElementSpace* GetFESpace() { return fespace.get();}
|
||||
|
||||
void SetZeroBdr(int bdr_attr)
|
||||
{
|
||||
zero_bdrs.insert(bdr_attr);
|
||||
}
|
||||
|
||||
void SetBdrLoad(int attr)
|
||||
{
|
||||
bdr_loads_markers.insert(attr);
|
||||
}
|
||||
|
||||
void SetVolForce(mfem::real_t period, mfem::real_t amplitude, mfem::real_t rad,
|
||||
mfem::real_t xc=0.0, mfem::real_t yc=0.0, mfem::real_t zc=0.0,
|
||||
mfem::real_t L=5.0, mfem::real_t t0=0.0, mfem::real_t n=2.0)
|
||||
{
|
||||
// copy data to the host
|
||||
mfem::real_t* pvol_force_mem = vol_force_mem.HostReadWrite();
|
||||
|
||||
pvol_force_mem[1] = period;
|
||||
pvol_force_mem[2] = amplitude;
|
||||
pvol_force_mem[3] = rad;
|
||||
pvol_force_mem[4] = xc;
|
||||
pvol_force_mem[5] = yc;
|
||||
pvol_force_mem[6] = zc;
|
||||
|
||||
pvol_force_mem[7] = L;
|
||||
pvol_force_mem[8] = t0;
|
||||
pvol_force_mem[9] = n;
|
||||
|
||||
// copy data to the device
|
||||
vol_force_mem.Read();
|
||||
}
|
||||
|
||||
void AddState(mfem::real_t t, mfem::Vector& state)
|
||||
{
|
||||
int cind=adjoint_data.ind % adjoint_data.max_states;
|
||||
adjoint_data.states[cind]=state;
|
||||
adjoint_data.times[cind]=t;
|
||||
adjoint_data.ind++;
|
||||
}
|
||||
|
||||
private:
|
||||
mfem::ParMesh &mesh;
|
||||
int order;
|
||||
|
||||
std::unique_ptr<mfem::FiniteElementCollection> fec;
|
||||
std::unique_ptr<mfem::ParFiniteElementSpace> fespace;
|
||||
int dim;
|
||||
int space_dim;
|
||||
|
||||
int myrank;
|
||||
|
||||
mfem::ParGridFunction *nodes;
|
||||
mfem::ParFiniteElementSpace *mfes;
|
||||
mfem::Array<int> domain_attributes;
|
||||
const mfem::IntegrationRule *ir;
|
||||
|
||||
mutable mfem::ParGridFunction displ;
|
||||
mutable mfem::ParGridFunction veloc;
|
||||
mutable mfem::ParGridFunction accel;
|
||||
|
||||
mutable mfem::BlockVector sol;
|
||||
mutable mfem::BlockVector rhs;
|
||||
mutable mfem::BlockVector tmp;
|
||||
mutable mfem::Vector res;
|
||||
|
||||
mfem::Array<int> block_true_offsets;
|
||||
|
||||
std::unique_ptr<mfem::future::UniformParameterSpace> ups;
|
||||
std::unique_ptr<mfem::QuadratureSpace> qs;
|
||||
std::unique_ptr<mfem::FaceQuadratureSpace> fqs;
|
||||
|
||||
// linear elasticty coefficients in dFEM form
|
||||
// l1, m1 - material 1
|
||||
// l2, m2 - material 2
|
||||
std::unique_ptr<mfem::CoefficientVector> l1, l2;
|
||||
std::unique_ptr<mfem::CoefficientVector> m1, m2;
|
||||
|
||||
// linear elasticity damping coefficients in dFEM form
|
||||
// strain proportional damping for material 1 and 2
|
||||
std::unique_ptr<mfem::CoefficientVector> dl1, dl2;
|
||||
std::unique_ptr<mfem::CoefficientVector> dm1, dm2;
|
||||
|
||||
// damping mass coefficients in dFEM form
|
||||
// (velocity proportional damping) for material 1 and 2
|
||||
std::unique_ptr<mfem::CoefficientVector> cm1, cm2;
|
||||
|
||||
// density coefficients in dFEM form for material 1 and 2
|
||||
std::unique_ptr<mfem::CoefficientVector> dens1, dens2;
|
||||
mfem::Coefficient *cdens1, *cdens2;
|
||||
|
||||
// density coefficient for topology optimization
|
||||
std::shared_ptr<mfem::CoefficientVector> density;
|
||||
mfem::Coefficient *cdensity;
|
||||
|
||||
static constexpr int FDispl = 0; //grid function displacement
|
||||
static constexpr int FVeloc = 1; //grid function velocity
|
||||
// elasticity Coefficient Vectors
|
||||
static constexpr int Lambda1 = 2, Lambda2 = 3, Mu1 = 4, Mu2 = 5;
|
||||
// damping Coefficient Vectors
|
||||
static constexpr int DLambda1 = 6, DMu1 = 7, DLambda2 = 8, DMu2 = 9;
|
||||
static constexpr int CMass1 = 10, CMass2 = 11; // damping mass coeff vectors
|
||||
static constexpr int Dens1 = 12, Dens2 = 13; // density Coefficient Vectors
|
||||
// density for topology optimization
|
||||
static constexpr int Density = 14; // coefficient vector
|
||||
static constexpr int Coords = 15; // coordinates grid function
|
||||
|
||||
// DFEM forward related definitions
|
||||
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_forward_op;
|
||||
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_mass_op;
|
||||
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_damp_op;
|
||||
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_vol_force_op;
|
||||
|
||||
std::unique_ptr<mfem::HypreParMatrix> M_lor;
|
||||
|
||||
std::unique_ptr<mfem::CGSolver> cg;
|
||||
std::unique_ptr<mfem::HypreBoomerAMG> amg;
|
||||
|
||||
// zero bdrs
|
||||
std::set<int> zero_bdrs;
|
||||
|
||||
// time dependent memory vector for dynamic force
|
||||
// the force is applied on all boundary attributes in bdr_loads_markers
|
||||
std::set<int> bdr_loads_markers;
|
||||
mutable mfem::Vector bdr_force_mem; // [0] - time, [1] - period, [2] - amplitude
|
||||
|
||||
// volumetric force parameters
|
||||
// A*sin(2*pi*t/T)*cos^n (pi (t-t_0)/L)
|
||||
// [0] - time, [1] - period, [2] - amplitude, [3] - radius
|
||||
// [4],[5],[6] - point coordinates of the center of the force application
|
||||
// [7] - L total train length - could be proportional to the period [1]
|
||||
// [8] - t_0 center of the train
|
||||
// [9] - n the envelope power
|
||||
mutable mfem::Vector vol_force_mem;
|
||||
|
||||
// zero bdr dofs - constructed during the corrsponding Assemble calls
|
||||
mfem::Array<int> ess_tdof_list;
|
||||
|
||||
//objective/constraints integrand
|
||||
//obj->Mult(x,y)
|
||||
//takes state vector s and returns y which consists of multiple objectives/constraints
|
||||
std::shared_ptr<mfem::Operator> obj;
|
||||
|
||||
struct
|
||||
{
|
||||
mfem::real_t Tfinal;
|
||||
mfem::Vector states[2];
|
||||
mfem::real_t times[2];
|
||||
int ind=0;
|
||||
int max_states=2;
|
||||
} adjoint_data;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class ExampleObjectiveIntegrand: public mfem::Operator
|
||||
{
|
||||
public:
|
||||
|
||||
ExampleObjectiveIntegrand(mfem::ParFiniteElementSpace* fes_,
|
||||
std::shared_ptr<mfem::Coefficient> objc);
|
||||
|
||||
void SetCoefficients( std::shared_ptr<mfem::Coefficient> objc);
|
||||
|
||||
//evaluates the QoIs y[1] for a given state x[2 x fes_->GetTrueVSize()]
|
||||
virtual void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
|
||||
|
||||
mfem::real_t EvalScalar(const mfem::Vector &x) const
|
||||
{
|
||||
mfem::Vector y(1);
|
||||
Mult(x, y);
|
||||
return y[0];
|
||||
}
|
||||
|
||||
void EvalGradient(const mfem::Vector &x, mfem::Vector &grad) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<mfem::Coefficient> co;
|
||||
mfem::ParFiniteElementSpace* fes;
|
||||
mutable mfem::ParGridFunction disp;
|
||||
mutable mfem::ParGridFunction velo;
|
||||
|
||||
mfem::Operator* grad;
|
||||
|
||||
static constexpr int FDispl = 0; //grid function displacement
|
||||
static constexpr int FVeloc = 1; //grid function velocity
|
||||
static constexpr int Density = 14; // coefficient vector
|
||||
static constexpr int Coords = 15; // coordinates grid function
|
||||
|
||||
// DFEM related definitions (3 objectives)
|
||||
std::unique_ptr<mfem::future::DifferentiableOperator> obj;
|
||||
|
||||
// density coefficient for computing the objective function
|
||||
std::shared_ptr<mfem::CoefficientVector> density;
|
||||
|
||||
mfem::Array<int> block_true_offsets;
|
||||
|
||||
//uniform parameter space
|
||||
std::unique_ptr<mfem::future::UniformParameterSpace> ups;
|
||||
//quadrature space for the coefficient
|
||||
std::unique_ptr<mfem::QuadratureSpace> qs;
|
||||
|
||||
mfem::ParGridFunction *nodes;
|
||||
mfem::ParFiniteElementSpace *mfes;
|
||||
mfem::Array<int> domain_attributes;
|
||||
const mfem::IntegrationRule *ir;
|
||||
|
||||
mutable mfem::Vector res;
|
||||
};
|
||||
|
||||
#endif // LINEAR_ELASTICITY_HPP
|
||||
@@ -0,0 +1,473 @@
|
||||
// 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 "linear_elasticity.hpp"
|
||||
|
||||
#include "../chpt/dynamic_checkpointing.hpp"
|
||||
#include "../chpt/fixed_slot_checkpoint_storage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
#ifdef NVTX_DEBUG_HPP
|
||||
#undef NVTX_COLOR
|
||||
#define NVTX_COLOR ::nvtx::kSalmon
|
||||
#include NVTX_DEBUG_HPP
|
||||
#else
|
||||
#define dbg(...)
|
||||
#endif
|
||||
|
||||
#define MESH_PATH MFEM_SOURCE_DIR "/miniapps/mtop/examples/"
|
||||
constexpr auto MESH_TRI = MESH_PATH "dyn_hex2d_tri.msh";
|
||||
constexpr auto MESH_QUAD = MESH_PATH "dyn_hex2d_quad.msh";
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct State
|
||||
{
|
||||
mfem::real_t time = 0.0; // time of the state
|
||||
mfem::real_t dt = 0.0;
|
||||
mfem::real_t obj = 0.0; // accumulated objective
|
||||
mfem::Vector v; // state of the system
|
||||
};
|
||||
|
||||
// Snapshot = *view* (non-owning) used only during Store() packing and Read() callback
|
||||
struct StateSnapshotView
|
||||
{
|
||||
mfem::real_t time = 0.0;
|
||||
mfem::real_t dt = 0.0;
|
||||
mfem::real_t obj = 0.0;
|
||||
// Points to n*sizeof(real_t) bytes:
|
||||
// - during Store(): points to current State::v data (host)
|
||||
// - during Read(): points into the storage slot bytes (valid only during callback)
|
||||
const std::byte *v_bytes = nullptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class StateSnapshotViewPacker
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
public:
|
||||
explicit StateSnapshotViewPacker(size_t n) : n(n)
|
||||
{
|
||||
MFEM_VERIFY(n > 0, "StateSnapshotViewPacker: n must be > 0.");
|
||||
}
|
||||
|
||||
std::size_t SlotBytes() const
|
||||
{
|
||||
return (size_t)(3 + n) * sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
void Pack(const StateSnapshotView &s, void *dst) const
|
||||
{
|
||||
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
|
||||
MFEM_VERIFY(s.v_bytes != nullptr, "Pack: snapshot v_bytes is null.");
|
||||
auto *b = static_cast<std::byte*>(dst);
|
||||
std::memcpy(b + 0*sizeof(mfem::real_t), &s.time, sizeof(mfem::real_t));
|
||||
std::memcpy(b + 1*sizeof(mfem::real_t), &s.dt, sizeof(mfem::real_t));
|
||||
std::memcpy(b + 2*sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
|
||||
std::memcpy(b + 3*sizeof(mfem::real_t), s.v_bytes, n * sizeof(mfem::real_t));
|
||||
}
|
||||
|
||||
// Important: Unpack returns a *view* pointing into src bytes (no allocation).
|
||||
void Unpack(const void *src, StateSnapshotView &ssv) const
|
||||
{
|
||||
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
|
||||
const auto *b = static_cast<const std::byte*>(src);
|
||||
std::memcpy(&ssv.time, b + 0*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
std::memcpy(&ssv.dt, b + 1*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
std::memcpy(&ssv.obj, b + 2*sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
ssv.v_bytes = b + 3*sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
size_t VectorSize() const { return n; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// struct AdjState
|
||||
// {
|
||||
// mfem::real_t time;
|
||||
// mfem::real_t obj;
|
||||
// mfem::Vector adj;
|
||||
// mfem::Vector grd;
|
||||
// };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief The IsoElasticyLambdaCoeff class converts E modulus of elasticity
|
||||
/// and Poisson's ratio to Lame's lambda coefficient
|
||||
class IsoElasticyLambdaCoeff : public mfem::Coefficient
|
||||
{
|
||||
mfem::Coefficient *E, *nu;
|
||||
|
||||
public:
|
||||
/// Constructor - takes as inputs E modulus and Poisson's ratio
|
||||
IsoElasticyLambdaCoeff(mfem::Coefficient *E,
|
||||
mfem::Coefficient *nu):
|
||||
E(E), nu(nu) { }
|
||||
|
||||
/// Evaluates the Lame's lambda coefficient
|
||||
real_t Eval(mfem::ElementTransformation &T,
|
||||
const mfem::IntegrationPoint &ip) override
|
||||
{
|
||||
const real_t EE = E->Eval(T, ip);
|
||||
const real_t nn = nu->Eval(T, ip);
|
||||
constexpr auto Lambda = [](const real_t E, const real_t nu)
|
||||
{
|
||||
return E * nu / (1.0 + nu) / (1.0 - 2.0 * nu);
|
||||
};
|
||||
return Lambda(EE, nn);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief The IsoElasticySchearCoeff class converts E modulus of elasticity
|
||||
/// and Poisson's ratio to Shear coefficient
|
||||
///
|
||||
class IsoElasticySchearCoeff : public mfem::Coefficient
|
||||
{
|
||||
mfem::Coefficient *E, *nu;
|
||||
|
||||
public:
|
||||
/// Constructor - takes as inputs E modulus and Poisson's ratio
|
||||
IsoElasticySchearCoeff(mfem::Coefficient *E_, mfem::Coefficient *nu_):
|
||||
E(E_), nu(nu_) { }
|
||||
|
||||
/// Evaluates the shear coefficient coefficient
|
||||
real_t Eval(mfem::ElementTransformation &T,
|
||||
const mfem::IntegrationPoint &ip) override
|
||||
{
|
||||
const real_t EE = E->Eval(T, ip);
|
||||
const real_t nn = nu->Eval(T, ip);
|
||||
constexpr auto Schear = [](const real_t E, const real_t nu)
|
||||
{
|
||||
return E / (2.0 * (1.0 + nu));
|
||||
};
|
||||
return Schear(EE, nn);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
dbg();
|
||||
// Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
Hypre::Init();
|
||||
|
||||
// Parse command-line options.
|
||||
const char *mesh_file = MESH_QUAD;
|
||||
const char *device_config = "cpu";
|
||||
int order = 3;
|
||||
bool mesh_tri = false;
|
||||
bool mesh_quad = false;
|
||||
int par_ref_levels = 1;
|
||||
// int max_steps = 1000;
|
||||
bool paraview = false;
|
||||
bool visualization = true;
|
||||
int ode_solver_type = 4;
|
||||
real_t Tfinal = 1.0;
|
||||
real_t dt = 0.005;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&mesh_tri, "-tri", "--triangular", "-no-tri",
|
||||
"--no-triangular", "Enable or not triangular mesh.");
|
||||
args.AddOption(&mesh_quad, "-quad", "--quadrilateral", "-no-quad",
|
||||
"--no-quadrilateral", "Enable or not quadrilateral mesh.");
|
||||
args.AddOption(&par_ref_levels, "-prl", "--par-ref-levels",
|
||||
"Number of parallel mesh refinement levels.");
|
||||
// args.AddOption(&max_steps, "-ms", "--max-steps",
|
||||
// "Maximum number of time steps.");
|
||||
args.AddOption(¶view, "-pa", "--paraview", "-no-pa",
|
||||
"--no-paraview", "Enable or not Paraview output.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization", "Enable or not visualization.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
|
||||
args.AddOption(&Tfinal, "-T", "--tfinal",
|
||||
"Terminate when accumulated time reaches Tfinal.");
|
||||
args.AddOption(&dt, "-dt", "--dt", "Time step.");
|
||||
|
||||
args.ParseCheck();
|
||||
|
||||
// Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (Mpi::Root()) { device.Print(); }
|
||||
|
||||
// Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_tri ? MESH_TRI : mesh_quad ? MESH_QUAD : mesh_file, 1, 1);
|
||||
const int dim = mesh.Dimension();
|
||||
|
||||
// Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1000 elements.
|
||||
{
|
||||
const int ref_levels =
|
||||
(int)floor(log(1000. / mesh.GetNE()) / log(2.) / dim);
|
||||
for (int l = 0; l < ref_levels; l++) { mesh.UniformRefinement(); }
|
||||
}
|
||||
if (Mpi::Root())
|
||||
{
|
||||
std::cout << "Number of elements: " << mesh.GetNE() << std::endl;
|
||||
}
|
||||
|
||||
// Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
for (int l = 0; l < par_ref_levels; l++) { pmesh.UniformRefinement(); }
|
||||
|
||||
// Allocate the time dependent linear elasticity operator
|
||||
LinearElasticityTimeDependentOperator lin_elasticity_op(pmesh, order);
|
||||
|
||||
// Set the material coefficients
|
||||
// density coefficient for topology optimization
|
||||
ConstantCoefficient rho_coef(0.5);
|
||||
|
||||
// Set elasticity coefficients for material 1 and 2
|
||||
ConstantCoefficient E1(0.1);
|
||||
ConstantCoefficient E2(1.0);
|
||||
ConstantCoefficient nu1(0.3);
|
||||
ConstantCoefficient nu2(0.3);
|
||||
|
||||
//Lame coefficients
|
||||
IsoElasticyLambdaCoeff lambda1(&E1, &nu1);
|
||||
IsoElasticySchearCoeff mu1(&E1, &nu1);
|
||||
IsoElasticyLambdaCoeff lambda2(&E2, &nu2);
|
||||
IsoElasticySchearCoeff mu2(&E2, &nu2);
|
||||
|
||||
// Set density coefficients for material 1 and 2
|
||||
ConstantCoefficient dens1_coef(0.5);
|
||||
ConstantCoefficient dens2_coef(1.0);
|
||||
|
||||
// Set damping coefficients
|
||||
ProductCoefficient cm1_coef(0.02, dens1_coef);
|
||||
ProductCoefficient cm2_coef(0.02, dens2_coef);
|
||||
|
||||
ProductCoefficient cl1_coef(0.01, lambda1);
|
||||
ProductCoefficient cmu1_coef(0.01, mu1);
|
||||
ProductCoefficient cl2_coef(0.01, lambda2);
|
||||
ProductCoefficient cmu2_coef(0.01, mu2);
|
||||
|
||||
lin_elasticity_op.SetElasticityCoefficients(lambda1, mu1, lambda2, mu2);
|
||||
|
||||
lin_elasticity_op.SetDensityMaterialCoefficients(dens1_coef, dens2_coef);
|
||||
|
||||
lin_elasticity_op.SetDampingMaterialCoefficients(cm1_coef, cm2_coef);
|
||||
lin_elasticity_op.SetDampingMaterialCoefficients(cl1_coef, cmu1_coef,
|
||||
cl2_coef, cmu2_coef);
|
||||
|
||||
lin_elasticity_op.SetDensity(rho_coef);
|
||||
|
||||
// set bottom bdr to zero (both the velocities and the displacements)
|
||||
lin_elasticity_op.SetZeroBdr(1);
|
||||
|
||||
lin_elasticity_op.SetVolForce(1.0 /*period*/, 1.0 /*amplitude*/, 0.2 /*radius*/,
|
||||
0.0 /*x center*/, 0.0 /*y center*/, 0.0 /*z center*/,
|
||||
5.0 /* train length*/, 2.5 /*center of the train*/, 2.0 /*power*/);
|
||||
|
||||
lin_elasticity_op.AssembleExplicit();
|
||||
|
||||
// test mult explicit
|
||||
{
|
||||
BlockVector tst;
|
||||
tst.UseDevice(true);
|
||||
tst.Update(lin_elasticity_op.GetTrueBlockOffsets());
|
||||
|
||||
tst = 0.0; // tst.Randomize();
|
||||
tst.Read();
|
||||
|
||||
BlockVector grd;
|
||||
grd.Update(lin_elasticity_op.GetTrueBlockOffsets());
|
||||
|
||||
grd = 0.0;
|
||||
|
||||
lin_elasticity_op.Mult(tst, grd);
|
||||
|
||||
lin_elasticity_op.GetVelocity().SetFromTrueDofs(grd.GetBlock(1));
|
||||
}
|
||||
|
||||
auto obj = std::make_shared<ExampleObjectiveIntegrand>
|
||||
(lin_elasticity_op.GetDisplacement().ParFESpace(),
|
||||
std::shared_ptr<Coefficient>());
|
||||
|
||||
// set the objective for the integration process
|
||||
lin_elasticity_op.SetObjective(obj);
|
||||
|
||||
ParaViewDataCollection paraview_dc("isoel", &pmesh);
|
||||
if (paraview)
|
||||
{
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(order);
|
||||
paraview_dc.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc.SetHighOrderOutput(true);
|
||||
paraview_dc.RegisterField("disp", &(lin_elasticity_op.GetDisplacement()));
|
||||
paraview_dc.RegisterField("velo", &(lin_elasticity_op.GetVelocity()));
|
||||
}
|
||||
|
||||
// 4. Define the ODE solver used for time integration.
|
||||
// Several explicit Runge-Kutta methods are available.
|
||||
auto ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
lin_elasticity_op.SetTime(0.0);
|
||||
ode_solver->Init(lin_elasticity_op);
|
||||
|
||||
// Forward computations
|
||||
{
|
||||
// number of snapshots to be stored by the checkpointing process
|
||||
const int s = 10;
|
||||
|
||||
// define the packer object
|
||||
const int max_slots = lin_elasticity_op.GetState().Size();
|
||||
StateSnapshotViewPacker packer(max_slots);
|
||||
|
||||
// storage stores StateSnapshotView snapshots using fixed-size slots
|
||||
using Storage = mfem::FixedSlotMemoryCheckpointStorage<
|
||||
/* Snapshot */ StateSnapshotView,
|
||||
/* Packer */ StateSnapshotViewPacker>;
|
||||
Storage storage(s, packer);
|
||||
|
||||
// Snapshot type is StateSnapshotView
|
||||
using Checkpointing = mfem::DynamicCheckpointing<
|
||||
/* Snapshot */ StateSnapshotView,
|
||||
/* Storage */ Storage>;
|
||||
Checkpointing ckpt(s, storage);
|
||||
|
||||
// Returns view of the State and avoids data transfer
|
||||
auto make_snapshot = [&](const State &u) -> StateSnapshotView
|
||||
{
|
||||
MFEM_VERIFY(u.v.Size() == max_slots, "make_snapshot: State.v size changed!");
|
||||
|
||||
// Ensure host access if MFEM device is in use:
|
||||
const mfem::real_t *vh = u.v.HostRead();
|
||||
|
||||
StateSnapshotView snap;
|
||||
snap.time = u.time;
|
||||
snap.obj = u.obj;
|
||||
snap.v_bytes = reinterpret_cast<const std::byte*>(vh);
|
||||
return snap;
|
||||
};
|
||||
|
||||
// Transfers data from the snaphot view to the State u_out.
|
||||
// auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
|
||||
// {
|
||||
// u_out.time = snap.time;
|
||||
// u_out.obj = snap.obj;
|
||||
// if (u_out.v.Size() != max_slots) { u_out.v.SetSize(max_slots); }
|
||||
// mfem::real_t *vh = u_out.v.HostWrite();
|
||||
// std::memcpy(vh,
|
||||
// snap.v_bytes,
|
||||
// (std::size_t)max_slots * sizeof(mfem::real_t));
|
||||
// //make sure that the date is on the device
|
||||
// u_out.v.Read(true);
|
||||
// };
|
||||
|
||||
using Step = mfem::DynamicCheckpointing<
|
||||
/* Snapshot */ StateSnapshotView,
|
||||
/* Storage */ Storage>::Step;
|
||||
|
||||
// execute one integration step
|
||||
auto primal_step = [&](State &u_st, Step i)
|
||||
{
|
||||
// begin with curent state u_st
|
||||
real_t t = u_st.time;
|
||||
real_t ldt = dt;
|
||||
real_t obj = u_st.obj;
|
||||
|
||||
// make sure the integration does not overjump Tfinal
|
||||
if ((t+ldt)>Tfinal)
|
||||
{
|
||||
ldt = Tfinal - t;
|
||||
}
|
||||
|
||||
// advance u_st
|
||||
ode_solver->Step(u_st.v, t, ldt);
|
||||
|
||||
// TO-DO update objective
|
||||
obj = u_st.v[u_st.v.Size()-1];
|
||||
|
||||
// return updated u_st
|
||||
u_st.dt = t - u_st.time;
|
||||
u_st.time = t;
|
||||
u_st.obj = obj;
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
mfem::out<<"t: "<<u_st.time<<" dt="<<u_st.dt<<" obj:"<<obj<<"\n";
|
||||
}
|
||||
};
|
||||
|
||||
State u;
|
||||
u.v.SetSize(lin_elasticity_op.GetState().Size());
|
||||
u.obj = 0.0;
|
||||
u.time = 0.0;
|
||||
u.dt = 0.0;
|
||||
|
||||
// set initial state to 0
|
||||
u.v = 0.0;
|
||||
|
||||
dbg("Forward sweep (unknown number of steps)");
|
||||
real_t t = 0.0;
|
||||
Step i = 0;
|
||||
|
||||
while (t<Tfinal)
|
||||
{
|
||||
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
|
||||
t = u.time;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
dbg("test objective gradients");
|
||||
{
|
||||
Vector state; state.SetSize(lin_elasticity_op.GetState().Size());
|
||||
state.Randomize();
|
||||
|
||||
Vector dx(state); dx.Randomize();
|
||||
Vector tmp(state);
|
||||
Vector grd(state);
|
||||
|
||||
const real_t ro = obj->EvalScalar(state);
|
||||
obj->EvalGradient(state,grd);
|
||||
|
||||
const real_t dp = InnerProduct(MPI_COMM_WORLD, grd, dx);
|
||||
// const real_t np=InnerProduct(MPI_COMM_WORLD,dx,dx);
|
||||
|
||||
real_t sca = 10.0;
|
||||
for (int i=0; i<10; i++)
|
||||
{
|
||||
sca = sca / 10.0;
|
||||
tmp.Set(sca, dx);
|
||||
tmp.Add(1.0, state);
|
||||
const real_t rc = obj->EvalScalar(tmp);
|
||||
if (Mpi::Root())
|
||||
{
|
||||
std::cout<<" obj="<<ro<<" true drv="<<dp<<" fd drv="<<(rc-ro)/(sca)<<std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
#include "linear_elasticity.hpp"
|
||||
|
||||
// #include "../chpt/dynamic_checkpointing.hpp"
|
||||
// #include "../chpt/fixed_slot_checkpoint_storage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
#ifdef NVTX_DEBUG_HPP
|
||||
#undef NVTX_COLOR
|
||||
#define NVTX_COLOR ::nvtx::kCyan
|
||||
#include NVTX_DEBUG_HPP
|
||||
#else
|
||||
#define dbg(...)
|
||||
#endif
|
||||
|
||||
constexpr auto MESH_TRI = MFEM_SOURCE_DIR
|
||||
"/miniapps/mtop/examples/dyn_hex2d_tri.msh";
|
||||
constexpr auto MESH_QUAD = MFEM_SOURCE_DIR
|
||||
"/miniapps/mtop/examples/dyn_hex2d_quad.msh";
|
||||
|
||||
struct State
|
||||
{
|
||||
mfem::real_t time = 0.0;
|
||||
mfem::real_t obj = 0.0;
|
||||
mfem::Vector v;
|
||||
};
|
||||
|
||||
// Snapshot = *view* (non-owning) used only during Store() packing and Read() callback
|
||||
struct StateSnapshotView
|
||||
{
|
||||
mfem::real_t time = 0.0;
|
||||
mfem::real_t obj = 0.0;
|
||||
|
||||
// Points to n*sizeof(real_t) bytes:
|
||||
// - during Store(): points to current State::v data (host)
|
||||
// - during Read(): points into the storage slot bytes (valid only during callback)
|
||||
const unsigned char *v_bytes = nullptr;
|
||||
};
|
||||
|
||||
class StateSnapshotViewPacker
|
||||
{
|
||||
public:
|
||||
explicit StateSnapshotViewPacker(int n) : n_(n)
|
||||
{
|
||||
MFEM_VERIFY(n_ > 0, "StateSnapshotViewPacker: n must be > 0.");
|
||||
}
|
||||
|
||||
std::size_t SlotBytes() const
|
||||
{
|
||||
return (std::size_t)(2 + n_) * sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
void Pack(const StateSnapshotView &s, void *dst) const
|
||||
{
|
||||
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
|
||||
MFEM_VERIFY(s.v_bytes != nullptr, "Pack: snapshot v_bytes is null.");
|
||||
|
||||
unsigned char *b = static_cast<unsigned char*>(dst);
|
||||
|
||||
std::memcpy(b, &s.time, sizeof(mfem::real_t));
|
||||
std::memcpy(b + sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
|
||||
|
||||
std::memcpy(b + 2*sizeof(mfem::real_t),
|
||||
s.v_bytes,
|
||||
(std::size_t)n_ * sizeof(mfem::real_t));
|
||||
}
|
||||
|
||||
// Important: Unpack returns a *view* pointing into src bytes (no allocation).
|
||||
void Unpack(const void *src, StateSnapshotView &out) const
|
||||
{
|
||||
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
|
||||
|
||||
const unsigned char *b = static_cast<const unsigned char*>(src);
|
||||
|
||||
std::memcpy(&out.time, b, sizeof(mfem::real_t));
|
||||
std::memcpy(&out.obj, b + sizeof(mfem::real_t), sizeof(mfem::real_t));
|
||||
|
||||
out.v_bytes = b + 2*sizeof(mfem::real_t);
|
||||
}
|
||||
|
||||
int Size() const { return n_; }
|
||||
|
||||
private:
|
||||
int n_ = 0;
|
||||
};
|
||||
|
||||
struct AdjState
|
||||
{
|
||||
mfem::real_t time;
|
||||
mfem::real_t obj;
|
||||
mfem::Vector adj;
|
||||
mfem::Vector grd;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief The IsoElasticyLambdaCoeff class converts E modulus of elasticity
|
||||
/// and Poisson's ratio to Lame's lambda coefficient
|
||||
class IsoElasticyLambdaCoeff : public mfem::Coefficient
|
||||
{
|
||||
mfem::Coefficient *E, *nu;
|
||||
|
||||
public:
|
||||
/// Constructor - takes as inputs E modulus and Poisson's ratio
|
||||
IsoElasticyLambdaCoeff(mfem::Coefficient *E,
|
||||
mfem::Coefficient *nu):
|
||||
E(E), nu(nu) { }
|
||||
|
||||
/// Evaluates the Lame's lambda coefficient
|
||||
real_t Eval(mfem::ElementTransformation &T,
|
||||
const mfem::IntegrationPoint &ip) override
|
||||
{
|
||||
const real_t EE = E->Eval(T, ip);
|
||||
const real_t nn = nu->Eval(T, ip);
|
||||
constexpr auto Lambda = [](const real_t E, const real_t nu)
|
||||
{
|
||||
return E * nu / (1.0 + nu) / (1.0 - 2.0 * nu);
|
||||
};
|
||||
return Lambda(EE, nn);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief The IsoElasticySchearCoeff class converts E modulus of elasticity
|
||||
/// and Poisson's ratio to Shear coefficient
|
||||
///
|
||||
class IsoElasticySchearCoeff : public mfem::Coefficient
|
||||
{
|
||||
mfem::Coefficient *E, *nu;
|
||||
|
||||
public:
|
||||
/// Constructor - takes as inputs E modulus and Poisson's ratio
|
||||
IsoElasticySchearCoeff(mfem::Coefficient *E_, mfem::Coefficient *nu_):
|
||||
E(E_), nu(nu_) { }
|
||||
|
||||
/// Evaluates the shear coefficient coefficient
|
||||
real_t Eval(mfem::ElementTransformation &T,
|
||||
const mfem::IntegrationPoint &ip) override
|
||||
{
|
||||
const real_t EE = E->Eval(T, ip);
|
||||
const real_t nn = nu->Eval(T, ip);
|
||||
constexpr auto Schear = [](const real_t E, const real_t nu)
|
||||
{
|
||||
return E / (2.0 * (1.0 + nu));
|
||||
};
|
||||
return Schear(EE, nn);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
dbg();
|
||||
|
||||
// Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
Hypre::Init();
|
||||
|
||||
// Parse command-line options.
|
||||
const char *mesh_file = MESH_QUAD;
|
||||
const char *device_config = "cpu";
|
||||
int order = 3;
|
||||
bool mesh_tri = false;
|
||||
bool mesh_quad = false;
|
||||
int par_ref_levels = 1;
|
||||
int max_steps = 1000;
|
||||
bool paraview = false;
|
||||
bool visualization = true;
|
||||
int ode_solver_type = 4;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&mesh_tri, "-tri", "--triangular", "-no-tri",
|
||||
"--no-triangular", "Enable or not triangular mesh.");
|
||||
args.AddOption(&mesh_quad, "-quad", "--quadrilateral", "-no-quad",
|
||||
"--no-quadrilateral", "Enable or not quadrilateral mesh.");
|
||||
args.AddOption(&par_ref_levels, "-prl", "--par-ref-levels",
|
||||
"Number of parallel mesh refinement levels.");
|
||||
args.AddOption(&max_steps, "-ms", "--max-steps",
|
||||
"Maximum number of time steps.");
|
||||
args.AddOption(¶view, "-pa", "--paraview", "-no-pa",
|
||||
"--no-paraview", "Enable or not Paraview output.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization", "Enable or not visualization.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
|
||||
args.ParseCheck();
|
||||
dbg("mesh_file: {}", mesh_file);
|
||||
|
||||
// Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (Mpi::Root()) { device.Print(); }
|
||||
|
||||
// Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_tri ? MESH_TRI : mesh_quad ? MESH_QUAD : mesh_file, 1, 1);
|
||||
const int dim = mesh.Dimension();
|
||||
|
||||
// Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1000 elements.
|
||||
{
|
||||
const int ref_levels =
|
||||
(int)floor(log(1000. / mesh.GetNE()) / log(2.) / dim);
|
||||
for (int l = 0; l < ref_levels; l++) { mesh.UniformRefinement(); }
|
||||
}
|
||||
if (Mpi::Root())
|
||||
{
|
||||
std::cout << "Number of elements: " << mesh.GetNE() << std::endl;
|
||||
dbg("Number of elements: {}", mesh.GetNE());
|
||||
}
|
||||
|
||||
// Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
for (int l = 0; l < par_ref_levels; l++) { pmesh.UniformRefinement(); }
|
||||
|
||||
// Allocate the time dependent linear elasticity operator
|
||||
LinearElasticityTimeDependentOperator lin_elasticity_op(pmesh, order);
|
||||
|
||||
// Set the material coefficients
|
||||
ConstantCoefficient rho_coef(
|
||||
0.5); // density coefficient for topology optimization
|
||||
|
||||
// Set elasticity coefficients for material 1 and 2
|
||||
ConstantCoefficient E1(0.1);
|
||||
ConstantCoefficient E2(1.0);
|
||||
ConstantCoefficient nu1(0.3);
|
||||
ConstantCoefficient nu2(0.3);
|
||||
|
||||
//Lame coefficients
|
||||
IsoElasticyLambdaCoeff lambda1(&E1, &nu1);
|
||||
IsoElasticySchearCoeff mu1(&E1, &nu1);
|
||||
IsoElasticyLambdaCoeff lambda2(&E2, &nu2);
|
||||
IsoElasticySchearCoeff mu2(&E2, &nu2);
|
||||
|
||||
// Set density coefficients for material 1 and 2
|
||||
ConstantCoefficient dens1_coef(0.5);
|
||||
ConstantCoefficient dens2_coef(1.0);
|
||||
|
||||
// Set damping coefficients
|
||||
ProductCoefficient cm1_coef(0.02, dens1_coef);
|
||||
ProductCoefficient cm2_coef(0.02, dens2_coef);
|
||||
|
||||
ProductCoefficient cl1_coef(0.01, lambda1);
|
||||
ProductCoefficient cmu1_coef(0.01, mu1);
|
||||
ProductCoefficient cl2_coef(0.01, lambda2);
|
||||
ProductCoefficient cmu2_coef(0.01, mu2);
|
||||
|
||||
lin_elasticity_op.SetElasticityCoefficients(lambda1, mu1, lambda2, mu2);
|
||||
|
||||
lin_elasticity_op.SetDensityMaterialCoefficients(dens1_coef, dens2_coef);
|
||||
|
||||
lin_elasticity_op.SetDampingMaterialCoefficients(cm1_coef, cm2_coef);
|
||||
lin_elasticity_op.SetDampingMaterialCoefficients(cl1_coef, cmu1_coef,
|
||||
cl2_coef, cmu2_coef);
|
||||
|
||||
lin_elasticity_op.SetDensity(rho_coef);
|
||||
|
||||
//set bottom bdr to zero (both the velocities and the displacements)
|
||||
lin_elasticity_op.SetZeroBdr(1);
|
||||
|
||||
lin_elasticity_op.SetVolForce(1.0 /*period*/, 1.0 /*amplitude*/, 0.2 /*radius*/,
|
||||
0.0 /*x center*/, 0.0 /*y center*/, 0.0 /*z center*/,
|
||||
5.0 /* train length*/, 2.5 /*center of the train*/, 2.0 /*power*/);
|
||||
|
||||
lin_elasticity_op.AssembleExplicit();
|
||||
|
||||
// test mult explicit
|
||||
{
|
||||
BlockVector tst; tst.Update(lin_elasticity_op.GetTrueBlockOffsets());
|
||||
tst=0.0; //tst.Randomize();
|
||||
tst.UseDevice(true); tst.Read();
|
||||
|
||||
BlockVector grd; grd.Update(lin_elasticity_op.GetTrueBlockOffsets());
|
||||
grd=0.0;
|
||||
lin_elasticity_op.Mult(tst,grd);
|
||||
lin_elasticity_op.GetVelocity().SetFromTrueDofs(grd.GetBlock(1));
|
||||
}
|
||||
|
||||
ParaViewDataCollection paraview_dc("isoel", &pmesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(order);
|
||||
paraview_dc.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc.SetHighOrderOutput(true);
|
||||
paraview_dc.RegisterField("disp", &(lin_elasticity_op.GetDisplacement()));
|
||||
paraview_dc.RegisterField("velo", &(lin_elasticity_op.GetVelocity()));
|
||||
|
||||
|
||||
/*
|
||||
{
|
||||
int s=10;
|
||||
|
||||
StateSnapshotViewPacker packer(lin_elasticity_op.GetState().Size());
|
||||
|
||||
// storage stores StateSnapshotView snapshots using fixed-size slots
|
||||
using Storage=mfem::FixedSlotMemoryCheckpointStorage<StateSnapshotView, StateSnapshotViewPacker>;
|
||||
Storage storage(s, packer);
|
||||
|
||||
auto make_snapshot = [&](const State &u) -> StateSnapshotView
|
||||
{
|
||||
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
|
||||
|
||||
// Ensure host access if MFEM device is in use:
|
||||
const mfem::real_t *vh = u.v.HostRead();
|
||||
|
||||
StateSnapshotView snap;
|
||||
snap.time = u.time;
|
||||
snap.obj = u.obj;
|
||||
snap.v_bytes = reinterpret_cast<const unsigned char*>(vh);
|
||||
return snap;
|
||||
};
|
||||
|
||||
auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
|
||||
{
|
||||
u_out.time = snap.time;
|
||||
u_out.obj = snap.obj;
|
||||
|
||||
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
|
||||
|
||||
mfem::real_t *vh = u_out.v.HostWrite();
|
||||
std::memcpy(vh,
|
||||
snap.v_bytes,
|
||||
(std::size_t)n * sizeof(mfem::real_t));
|
||||
};
|
||||
|
||||
|
||||
using Step = mfem::DynamicCheckpointing<StateSnapshotView, Storage>::Step;
|
||||
State u;
|
||||
u.v.SetSize(lin_elasticity_op.GetState().Size());
|
||||
|
||||
|
||||
|
||||
|
||||
auto primal_step = [&](StateCheckPoint &u, Step i)
|
||||
{
|
||||
if (Mpi::Root()){
|
||||
std::cout<<"Primal step: time= "<<u.time<<" obj= "<<u.obj;
|
||||
}
|
||||
const double dt = 0.01;
|
||||
u.obj=dt*i;
|
||||
u.time=dt*i;
|
||||
u.v=(mfem::real_t)i;
|
||||
|
||||
if (Mpi::Root()){
|
||||
std::cout<<" out Step: "<<i<<" time="<<u.time<<" obj="<<u.obj<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
auto adjoint_step = [&](AdjState &lambda, const StateCheckPoint &u_i, Step i)
|
||||
{
|
||||
const double dt = 0.01;
|
||||
MFEM_ASSERT(lambda.adj.Size() == u_i.state.Size(), "lambda and u_i size mismatch.");
|
||||
if (Mpi::Root()){
|
||||
std::cout<<"Adj step: time= "<<u_i.time<<" obj= "<<u_i.obj;
|
||||
std::cout<<" adj time= "<<lambda.time<<" adj obj="<<lambda.obj<<std::endl;
|
||||
}
|
||||
|
||||
lambda.obj=-u_i.obj;
|
||||
lambda.time=lambda.time-dt;
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Initial condition
|
||||
StateCheckPoint spt; spt.obj=-1.0; spt.time=-1.0; spt.state=(lin_elasticity_op.GetState());
|
||||
|
||||
Step i=0;
|
||||
mfem::real_t t=0.0;
|
||||
mfem::real_t dt=0.01;
|
||||
while(t<0.2)
|
||||
{
|
||||
ckpt.ForwardStep(i, spt, primal_step, make_snapshot);
|
||||
t=t+dt;
|
||||
++i;
|
||||
}
|
||||
|
||||
const Step m = i;
|
||||
if (Mpi::Root()){
|
||||
std::cout<<" Total number of steps="<<m<<std::endl;
|
||||
}
|
||||
|
||||
AdjState ast; ast.obj=1.0; ast.time=spt.time;
|
||||
ast.adj=(lin_elasticity_op.GetState());
|
||||
ast.grd=(lin_elasticity_op.GetState());
|
||||
|
||||
for (Step j = m - 1; j >= 0; --j)
|
||||
{
|
||||
if (Mpi::Root()){
|
||||
std::cout<<" Outer steps="<<j<<std::endl;
|
||||
}
|
||||
|
||||
ckpt.BackwardStep(j, ast, spt, primal_step, adjoint_step, make_snapshot, restore_snapshot);
|
||||
|
||||
if (j == 0) { break; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
//test time integration
|
||||
{
|
||||
real_t t = 0.0;
|
||||
|
||||
BlockVector tsol; tsol.Update(lin_elasticity_op.GetTrueBlockOffsets());
|
||||
//set initial conditions at time t
|
||||
tsol=0.0;
|
||||
|
||||
// 4. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
lin_elasticity_op.SetTime(t);
|
||||
ode_solver->Init(lin_elasticity_op);
|
||||
|
||||
paraview_dc.SetCycle(0);
|
||||
paraview_dc.SetTime(0.0);
|
||||
lin_elasticity_op.GetVelocity().SetFromTrueDofs(tsol.GetBlock(1));
|
||||
lin_elasticity_op.GetDisplacement().SetFromTrueDofs(tsol.GetBlock(0));
|
||||
paraview_dc.Save();
|
||||
|
||||
real_t dt_real = 0.005;
|
||||
//ode_solver->Run(tsol, t, dt_real, 1.0);
|
||||
|
||||
for (int i=0; i< std::min(max_steps, 6000); i++)
|
||||
{
|
||||
dbg("#{}", i);
|
||||
ode_solver->Step(tsol, t, dt_real);
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
std::cout << "t: " << t << std::endl;
|
||||
}
|
||||
|
||||
if (paraview && (i%5)==0)
|
||||
{
|
||||
dbg("Paraview output at step {} time {}", i, t);
|
||||
paraview_dc.SetCycle(i+1);
|
||||
paraview_dc.SetTime(t);
|
||||
lin_elasticity_op.GetVelocity().SetFromTrueDofs(tsol.GetBlock(1));
|
||||
lin_elasticity_op.GetDisplacement().SetFromTrueDofs(tsol.GetBlock(0));
|
||||
paraview_dc.Save();
|
||||
}
|
||||
|
||||
if (socketstream glvis; visualization && (i%10)==0 &&
|
||||
((glvis.open("localhost", 19916)),
|
||||
(glvis.is_open() ? glvis.precision(8) : 0),
|
||||
(glvis.is_open() ? (glvis << "mgjR" << std::endl, 0) : 0),
|
||||
(glvis.is_open())))
|
||||
{
|
||||
dbg("GLVis output at step {} time {}", i, t);
|
||||
glvis << "parallel " << Mpi::WorldSize() << " " << Mpi::WorldRank() << "\n";
|
||||
|
||||
lin_elasticity_op.GetVelocity().SetFromTrueDofs(tsol.GetBlock(1));
|
||||
ParGridFunction velocity(lin_elasticity_op.GetVelocity());
|
||||
|
||||
// lin_elasticity_op.GetDisplacement().SetFromTrueDofs(tsol.GetBlock(0));
|
||||
// ParGridFunction displacement(lin_elasticity_op.GetDisplacement());
|
||||
|
||||
glvis << "solution\n" << pmesh << velocity << std::flush;
|
||||
glvis << "keys g\n" << std::flush; // pause
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
if (paraview)
|
||||
{
|
||||
ParaViewDataCollection paraview_dc("isoel", &pmesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(order);
|
||||
paraview_dc.SetDataFormat(VTKFormat::BINARY);
|
||||
paraview_dc.SetHighOrderOutput(true);
|
||||
paraview_dc.SetCycle(0);
|
||||
paraview_dc.SetTime(0.0);
|
||||
//paraview_dc.RegisterField("disp", &sol);
|
||||
paraview_dc.RegisterField("disp", &(lin_elasticity_op.GetDisplacement()));
|
||||
paraview_dc.RegisterField("velo", &(lin_elasticity_op.GetVelocity()));
|
||||
paraview_dc.Save();
|
||||
}
|
||||
*/
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -264,7 +264,7 @@ int main(int argc, char *argv[])
|
||||
// parallel degrees of freedom, with offsets given by array true_offset.
|
||||
FiniteElementCollection *fec = nullptr;
|
||||
NURBSExtension *NURBSext = nullptr;
|
||||
if (mesh->NURBSext)
|
||||
if (pmesh->NURBSext)
|
||||
{
|
||||
NURBSext = new NURBSExtension(pmesh->NURBSext, order);
|
||||
fec = new NURBSFECollection(order);
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
// (2) Dzanic et al., "A method for bounding high-order finite element
|
||||
// functions: Applications to mesh validity and bounds-preserving limiters".
|
||||
//
|
||||
// We also use a recursive subdivision strategy to compute tighter estimate of
|
||||
// the function extremum.
|
||||
//
|
||||
// Compile with: make gridfunction-bounds
|
||||
//
|
||||
@@ -31,9 +33,6 @@
|
||||
// mpirun -np 4 gridfunction-bounds -nb 100 -ref 5 -bt 2 -l2
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
@@ -56,6 +55,8 @@ int main (int argc, char *argv[])
|
||||
int b_type = -1;
|
||||
bool continuous = true;
|
||||
int nbrute = 0;
|
||||
int rec_depth = 4;
|
||||
real_t rel_tol = 1e-4;
|
||||
|
||||
// Parse command-line options.
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -83,6 +84,12 @@ int main (int argc, char *argv[])
|
||||
args.AddOption(&nbrute, "-nb", "--nbrute",
|
||||
"Brute force search for minimum in an array of nxnxn points "
|
||||
"in each element.");
|
||||
args.AddOption(&rec_depth, "-rd", "--rec-depth",
|
||||
"Maximum depth for recursive subdivision to compute function "
|
||||
"extremum.");
|
||||
args.AddOption(&rel_tol, "-rt", "--rel-tol",
|
||||
"Relative tolerance for termination of recursive "
|
||||
"subdivision.");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1, false);
|
||||
@@ -151,7 +158,19 @@ int main (int argc, char *argv[])
|
||||
ParGridFunction lowerb(&fes_pc), upperb(&fes_pc);
|
||||
|
||||
// Compute bounds
|
||||
pfunc_proj->GetElementBounds(lowerb, upperb, ref);
|
||||
PLBound plb = pfunc_proj->GetElementBounds(lowerb, upperb, ref);
|
||||
|
||||
// Compute minimum and maximum bounds via recursion
|
||||
Vector bound_rec_min(vdim), bound_rec_max(vdim);
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
auto min_interval = pfunc_proj->EstimateFunctionMinimum(d, plb, rec_depth,
|
||||
rel_tol);
|
||||
auto max_interval = pfunc_proj->EstimateFunctionMaximum(d, plb, rec_depth,
|
||||
rel_tol);
|
||||
bound_rec_min(d) = min_interval.first;
|
||||
bound_rec_max(d) = max_interval.second;
|
||||
}
|
||||
|
||||
Vector bound_min(vdim), bound_max(vdim);
|
||||
for (int d = 0; d < vdim; d++)
|
||||
@@ -236,17 +255,31 @@ int main (int argc, char *argv[])
|
||||
{
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
cout << "Brute force and bounding comparison for component " <<
|
||||
cout << "Compare function extremum for component " <<
|
||||
d << endl;
|
||||
cout << "Brute force minimum and minimum bound: " << global_min(d)
|
||||
<< " " << bound_min(d) << endl;
|
||||
|
||||
cout << "Brute force maximum and maximum bound: " << global_max(d)
|
||||
<< " " << bound_max(d) << endl;
|
||||
|
||||
cout << "The difference in bounds is: " <<
|
||||
global_min(d)-bound_min(d) << " " <<
|
||||
bound_max(d)-global_max(d) << endl;
|
||||
constexpr int w = 20;
|
||||
cout << left << setw(w) << " "
|
||||
<< setw(w) << "Brute force"
|
||||
<< setw(w) << "PL Bound"
|
||||
<< setw(w) << "PL Bound + recursion" << endl
|
||||
<< left << setw(w) << "Minimum: "
|
||||
<< setw(w) << global_min(d)
|
||||
<< setw(w) << bound_min(d)
|
||||
<< setw(w) << bound_rec_min(d) << endl
|
||||
<< left << setw(w) << "Difference: "
|
||||
<< setw(w) << "-"
|
||||
<< setw(w) << global_min(d)-bound_min(d)
|
||||
<< setw(w) << global_min(d)-bound_rec_min(d) << endl;
|
||||
cout << endl
|
||||
<< left << setw(w) << "Maximum: "
|
||||
<< setw(w) << global_max(d)
|
||||
<< setw(w) << bound_max(d)
|
||||
<< setw(w) << bound_rec_max(d) << endl
|
||||
<< left << setw(w) << "Difference: "
|
||||
<< setw(w) << "-"
|
||||
<< setw(w) << bound_max(d)-global_max(d)
|
||||
<< setw(w) << bound_rec_max(d)-global_max(d) << endl;
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,10 +288,19 @@ int main (int argc, char *argv[])
|
||||
{
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
cout << "Minimum bound for component " << d << " is " <<
|
||||
bound_min(d) << endl;
|
||||
cout << "Maximum bound for component " << d << " is " <<
|
||||
bound_max(d) << endl;
|
||||
cout << "Compare function extremum for component " <<
|
||||
d << endl;
|
||||
constexpr int w = 20;
|
||||
cout << left << setw(w) << " "
|
||||
<< setw(w) << "PL Bound"
|
||||
<< setw(w) << "PL Bound + recursion" << endl
|
||||
<< left << setw(w) << "Minimum: "
|
||||
<< setw(w) << bound_min(d)
|
||||
<< setw(w) << bound_rec_min(d) << endl;
|
||||
cout << endl
|
||||
<< left << setw(w) << "Maximum: "
|
||||
<< setw(w) << bound_max(d)
|
||||
<< setw(w) << bound_rec_max(d) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ set(UNIT_TESTS_SRCS
|
||||
dfem/test_divergence.cpp
|
||||
dfem/test_lvector_interface.cpp
|
||||
dfem/test_mass.cpp
|
||||
dfem/test_transpose.cpp
|
||||
dfem/test_multiple_integrators.cpp
|
||||
general/test_array.cpp
|
||||
general/test_scan.cpp
|
||||
general/test_arrays_by_name.cpp
|
||||
|
||||
@@ -218,7 +218,7 @@ template <int DIM> void mass_mat_mixed(const char* filename, int p)
|
||||
|
||||
SECTION("spmat")
|
||||
{
|
||||
SparseMatrix *A;
|
||||
SparseMatrix *A = nullptr;
|
||||
ddopdu->Assemble(A);
|
||||
TestSameMatrices(*A, blf.SpMat());
|
||||
delete A;
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
// 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 "../unit_tests.hpp"
|
||||
#include "../linalg/test_same_matrices.hpp"
|
||||
#include "mfem.hpp"
|
||||
#include "fem/dfem/doperator.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
using dscalar_t = real_t;
|
||||
#else
|
||||
using mfem::future::dual;
|
||||
using dscalar_t = dual<real_t, real_t>;
|
||||
#endif
|
||||
|
||||
template <int DIM>
|
||||
void mult_integ(const char *filename, int p)
|
||||
{
|
||||
CAPTURE(filename, DIM, p);
|
||||
|
||||
Mesh smesh(filename);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, smesh);
|
||||
pmesh.EnsureNodes();
|
||||
auto* nodes = static_cast<ParGridFunction*>(pmesh.GetNodes());
|
||||
p = std::max(p, pmesh.GetNodalFESpace()->GetMaxElementOrder());
|
||||
smesh.Clear();
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace fes(&pmesh, &fec);
|
||||
|
||||
ParGridFunction x(&fes), y(&fes), z(&fes);
|
||||
Vector X(fes.GetTrueVSize()), Y(fes.GetTrueVSize()), Z(fes.GetTrueVSize());
|
||||
|
||||
X.Randomize(1);
|
||||
x.SetFromTrueDofs(X);
|
||||
|
||||
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(), 2 * p);
|
||||
|
||||
Array<int> all_domain_attr;
|
||||
if (pmesh.attributes.Size() > 0)
|
||||
{
|
||||
all_domain_attr.SetSize(pmesh.attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
}
|
||||
|
||||
ParBilinearForm blf(&fes);
|
||||
blf.AddDomainIntegrator(new DiffusionIntegrator(ir));
|
||||
blf.AddDomainIntegrator(new MassIntegrator(ir));
|
||||
blf.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
blf.Assemble();
|
||||
blf.Mult(x, y);
|
||||
fes.GetProlongationMatrix()->MultTranspose(y, Y);
|
||||
|
||||
const auto mass_qf =
|
||||
[] MFEM_HOST_DEVICE(
|
||||
const dscalar_t &u,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
return tuple{u * w * det(J)};
|
||||
};
|
||||
|
||||
const auto diffusion_qf =
|
||||
[] MFEM_HOST_DEVICE(
|
||||
const tensor<dscalar_t, DIM> &dudxi,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
return tuple{(dudxi * inv(J)) * transpose(inv(J)) * w * det(J)};
|
||||
};
|
||||
|
||||
static constexpr int U = 0, Coords = 1;
|
||||
const auto sol = std::vector{ FieldDescriptor{ U, &fes } };
|
||||
DifferentiableOperator dop(sol, {{Coords, nodes->ParFESpace()}}, pmesh);
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, U> {};
|
||||
|
||||
dop.AddDomainIntegrator(diffusion_qf,
|
||||
tuple{ Gradient<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Gradient<U>{} },
|
||||
*ir, all_domain_attr, derivatives);
|
||||
|
||||
dop.AddDomainIntegrator(mass_qf,
|
||||
tuple{ Value<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Value<U>{} },
|
||||
*ir, all_domain_attr, derivatives);
|
||||
|
||||
SECTION("action")
|
||||
{
|
||||
dop.SetParameters({ nodes });
|
||||
|
||||
fes.GetRestrictionMatrix()->Mult(x, X);
|
||||
dop.Mult(X, Z);
|
||||
|
||||
Y -= Z;
|
||||
real_t norm_g, norm_l = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
SECTION("linearized action")
|
||||
{
|
||||
auto ddopdu = dop.GetDerivative(U, {&x}, {nodes});
|
||||
|
||||
fes.GetRestrictionMatrix()->Mult(x, X);
|
||||
ddopdu->Mult(X, Z);
|
||||
|
||||
Y -= Z;
|
||||
real_t norm_g, norm_l = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
SECTION("linearized assembled SparseMatrix")
|
||||
{
|
||||
auto ddopdu = dop.GetDerivative(U, {&x}, {nodes});
|
||||
|
||||
SparseMatrix *A = nullptr;
|
||||
ddopdu->Assemble(A);
|
||||
|
||||
A->Mult(x, z);
|
||||
fes.GetProlongationMatrix()->MultTranspose(z, Z);
|
||||
|
||||
Y -= Z;
|
||||
real_t norm_g, norm_l = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
delete A;
|
||||
}
|
||||
|
||||
SECTION("linearized assembled HypreParMatrix")
|
||||
{
|
||||
auto ddopdu = dop.GetDerivative(U, {&x}, {nodes});
|
||||
|
||||
HypreParMatrix *A = nullptr;
|
||||
ddopdu->Assemble(A);
|
||||
|
||||
fes.GetRestrictionMatrix()->Mult(x, X);
|
||||
A->Mult(X, Z);
|
||||
|
||||
Y -= Z;
|
||||
real_t norm_g, norm_l = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
delete A;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// no GPU tag to avoid failing 'hypre parallel mat' section
|
||||
TEST_CASE("dFEM Multiple Integrators", "[Parallel][dFEM][XXX]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
|
||||
|
||||
SECTION("2d")
|
||||
{
|
||||
const auto filename2d =
|
||||
GENERATE(
|
||||
"../../data/star.mesh",
|
||||
"../../data/star-q3.mesh",
|
||||
"../../data/rt-2d-q3.mesh",
|
||||
"../../data/inline-quad.mesh",
|
||||
"../../data/periodic-square.mesh"
|
||||
);
|
||||
mult_integ<2>(filename2d, p);
|
||||
}
|
||||
|
||||
SECTION("3d")
|
||||
{
|
||||
const auto filename3d =
|
||||
GENERATE(
|
||||
"../../data/fichera.mesh",
|
||||
"../../data/fichera-q3.mesh",
|
||||
"../../data/inline-hex.mesh",
|
||||
"../../data/toroid-hex.mesh",
|
||||
"../../data/periodic-cube.mesh"
|
||||
);
|
||||
mult_integ<3>(filename3d, p);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,409 @@
|
||||
// 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 "../unit_tests.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#include "fem/dfem/doperator.hpp" // TODO: remove before merge
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
using dscalar_t = real_t;
|
||||
#else
|
||||
using mfem::future::dual;
|
||||
using dscalar_t = dual<real_t, real_t>;
|
||||
#endif
|
||||
|
||||
template <int DIM>
|
||||
void transpose(const char *filename, int p)
|
||||
{
|
||||
CAPTURE(filename, DIM, p);
|
||||
|
||||
Mesh serial_mesh(filename);
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
serial_mesh.Clear();
|
||||
mesh.EnsureNodes();
|
||||
auto* nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
|
||||
p = std::max(p, mesh.GetNodalFESpace()->GetMaxElementOrder());
|
||||
|
||||
Array<int> all_domain_attr;
|
||||
if (mesh.attributes.Size() > 0)
|
||||
{
|
||||
all_domain_attr.SetSize(mesh.attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
}
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace scalar_fes(&mesh, &fec);
|
||||
ParFiniteElementSpace vector_fes(&mesh, &fec, DIM);
|
||||
|
||||
ParGridFunction sgf(&scalar_fes);
|
||||
|
||||
auto f0 = [](const Vector &x)
|
||||
{
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
return M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]) * sin(M_PI*x[2]);
|
||||
}
|
||||
return M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]);
|
||||
};
|
||||
|
||||
FunctionCoefficient f0_coeff(f0);
|
||||
sgf.ProjectCoefficient(f0_coeff);
|
||||
|
||||
ParGridFunction vgf(&vector_fes);
|
||||
|
||||
auto gradf1 = [](const Vector &x, Vector &u)
|
||||
{
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
u(0) = M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]) * sin(M_PI*x[2]);
|
||||
u(1) = M_PI*sin(M_PI*x[0]) * cos(M_PI*x[1]) * sin(M_PI*x[2]);
|
||||
u(2) = M_PI*sin(M_PI*x[0]) * sin(M_PI*x[1]) * cos(M_PI*x[2]);
|
||||
return;
|
||||
}
|
||||
u(0) = M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]);
|
||||
u(1) = M_PI*sin(M_PI*x[0]) * cos(M_PI*x[1]);
|
||||
};
|
||||
|
||||
VectorFunctionCoefficient gradf1_coeff(DIM, gradf1);
|
||||
vgf.ProjectCoefficient(gradf1_coeff);
|
||||
|
||||
const auto* ir = &IntRules.Get(mesh.GetTypicalElementGeometry(), 2 * p);
|
||||
|
||||
SECTION("Mass Transpose Action")
|
||||
{
|
||||
ParBilinearForm Mblf(&scalar_fes);
|
||||
auto mass_integ = new MassIntegrator;
|
||||
mass_integ->SetIntegrationRule(*ir);
|
||||
Mblf.AddDomainIntegrator(mass_integ);
|
||||
Mblf.Assemble();
|
||||
Mblf.Finalize();
|
||||
auto Mmat = Mblf.ParallelAssemble();
|
||||
|
||||
static constexpr int SCALAR = 0, COORDINATES = 1;
|
||||
const auto sol = std::vector{FieldDescriptor{SCALAR, &scalar_fes}};
|
||||
const auto par = std::vector{FieldDescriptor{COORDINATES, nodes->ParFESpace()}};
|
||||
DifferentiableOperator dop(sol, par, mesh);
|
||||
const auto gradient_qf = [] MFEM_HOST_DEVICE(
|
||||
const dscalar_t &u,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
return tuple{u * w * det(J)};
|
||||
};
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, SCALAR> {};
|
||||
dop.AddDomainIntegrator(gradient_qf,
|
||||
tuple{Value<SCALAR>{}, Gradient<COORDINATES>{}, Weight{}},
|
||||
tuple{Value<SCALAR>{}},
|
||||
*ir, all_domain_attr, derivatives);
|
||||
dop.SetParameters({nodes});
|
||||
|
||||
Vector S, T, U;
|
||||
S.SetSize(scalar_fes.GetTrueVSize());
|
||||
T.SetSize(scalar_fes.GetTrueVSize());
|
||||
U.SetSize(scalar_fes.GetTrueVSize());
|
||||
|
||||
sgf.GetTrueDofs(S);
|
||||
|
||||
Mmat->MultTranspose(S, T);
|
||||
|
||||
auto ddop = dop.GetDerivative(SCALAR, {&sgf}, {nodes});
|
||||
ddop->MultTranspose(S, U);
|
||||
|
||||
T -= U;
|
||||
real_t norm_g, norm_l = T.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
|
||||
delete Mmat;
|
||||
}
|
||||
|
||||
SECTION("Vector Mass Transpose Action")
|
||||
{
|
||||
ParBilinearForm Mvblf(&vector_fes);
|
||||
auto mass_integ = new VectorMassIntegrator;
|
||||
mass_integ->SetIntegrationRule(*ir);
|
||||
Mvblf.AddDomainIntegrator(mass_integ);
|
||||
Mvblf.Assemble();
|
||||
Mvblf.Finalize();
|
||||
auto Mvmat = Mvblf.ParallelAssemble();
|
||||
|
||||
static constexpr int VECTOR = 0, COORDINATES = 1;
|
||||
const auto sol = std::vector{FieldDescriptor{VECTOR, &vector_fes}};
|
||||
const auto par = std::vector{FieldDescriptor{COORDINATES, nodes->ParFESpace()}};
|
||||
DifferentiableOperator dop(sol, par, mesh);
|
||||
const auto gradient_qf = [] MFEM_HOST_DEVICE(
|
||||
const tensor<dscalar_t, DIM> &u,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
return tuple{u * w * det(J)};
|
||||
};
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, VECTOR> {};
|
||||
dop.AddDomainIntegrator(gradient_qf,
|
||||
tuple{Value<VECTOR>{}, Gradient<COORDINATES>{}, Weight{}},
|
||||
tuple{Value<VECTOR>{}},
|
||||
*ir, all_domain_attr, derivatives);
|
||||
dop.SetParameters({nodes});
|
||||
|
||||
Vector V, W, Z;
|
||||
V.SetSize(vector_fes.GetTrueVSize());
|
||||
W.SetSize(vector_fes.GetTrueVSize());
|
||||
Z.SetSize(vector_fes.GetTrueVSize());
|
||||
|
||||
vgf.GetTrueDofs(V);
|
||||
|
||||
Mvmat->MultTranspose(V, W);
|
||||
|
||||
auto ddop = dop.GetDerivative(VECTOR, {&vgf}, {nodes});
|
||||
ddop->MultTranspose(V, Z);
|
||||
|
||||
W -= Z;
|
||||
real_t norm_g, norm_l = W.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
|
||||
delete Mvmat;
|
||||
}
|
||||
|
||||
SECTION("Discrete Gradient Transpose Action")
|
||||
{
|
||||
ParMixedBilinearForm Gblf(&scalar_fes, &vector_fes);
|
||||
auto grad_integ = new GradientIntegrator;
|
||||
grad_integ->SetIntegrationRule(*ir);
|
||||
Gblf.AddDomainIntegrator(grad_integ);
|
||||
Gblf.Assemble();
|
||||
Gblf.Finalize();
|
||||
auto Gmat = Gblf.ParallelAssemble();
|
||||
|
||||
static constexpr int SCALAR = 0, VECTOR = 2, COORDINATES = 1;
|
||||
const auto sol = std::vector{FieldDescriptor{SCALAR, &scalar_fes}};
|
||||
const auto par = std::vector
|
||||
{
|
||||
FieldDescriptor{VECTOR, &vector_fes},
|
||||
FieldDescriptor{COORDINATES, nodes->ParFESpace()}
|
||||
};
|
||||
DifferentiableOperator dop(sol, par, mesh);
|
||||
const auto gradient_qf = [] MFEM_HOST_DEVICE(
|
||||
const tensor<dscalar_t, DIM> &dudxi,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
const auto dudx = dudxi * inv(J);
|
||||
return tuple{dudx * w * det(J)};
|
||||
};
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, SCALAR> {};
|
||||
dop.AddDomainIntegrator(gradient_qf,
|
||||
tuple{Gradient<SCALAR>{}, Gradient<COORDINATES>{}, Weight{}},
|
||||
tuple{Value<VECTOR>{}},
|
||||
*ir, all_domain_attr, derivatives);
|
||||
dop.SetParameters({&vgf, nodes});
|
||||
|
||||
Vector S, T, V;
|
||||
S.SetSize(scalar_fes.GetTrueVSize());
|
||||
T.SetSize(scalar_fes.GetTrueVSize());
|
||||
vgf.GetTrueDofs(V);
|
||||
|
||||
Gmat->MultTranspose(V, S);
|
||||
|
||||
auto ddop = dop.GetDerivative(SCALAR, {&sgf}, {&vgf, nodes});
|
||||
ddop->MultTranspose(V, T);
|
||||
|
||||
S -= T;
|
||||
real_t norm_g, norm_l = S.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
|
||||
delete Gmat;
|
||||
}
|
||||
|
||||
SECTION("Scalar Convection Transpose Action")
|
||||
{
|
||||
auto b_func = [](const Vector &x, Vector &b)
|
||||
{
|
||||
b(0) = cos(x[0] * 2.0 * M_PI);
|
||||
b(1) = 1.0 + cos(x[1] * 2.0 * M_PI);
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
b(2) = 2.0 + cos(x[2] * 2.0 * M_PI);
|
||||
}
|
||||
};
|
||||
VectorFunctionCoefficient b_coeff(DIM, b_func);
|
||||
|
||||
ParBilinearForm Gblf(&scalar_fes);
|
||||
auto conv_integ = new ConvectionIntegrator(b_coeff);
|
||||
conv_integ->SetIntegrationRule(*ir);
|
||||
Gblf.AddDomainIntegrator(conv_integ);
|
||||
Gblf.Assemble();
|
||||
Gblf.Finalize();
|
||||
auto Gmat = Gblf.ParallelAssemble();
|
||||
|
||||
static constexpr int SCALAR = 0, COORDINATES = 1;
|
||||
const auto sol = std::vector{FieldDescriptor{SCALAR, &scalar_fes}};
|
||||
const auto par = std::vector
|
||||
{
|
||||
FieldDescriptor{COORDINATES, nodes->ParFESpace()}
|
||||
};
|
||||
DifferentiableOperator dop(sol, par, mesh);
|
||||
|
||||
const auto convection_qf =
|
||||
[] MFEM_HOST_DEVICE(
|
||||
const tensor<dscalar_t, DIM> &dudxi,
|
||||
const tensor<real_t, DIM> &x,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
const auto dudx = dudxi * inv(J);
|
||||
tensor<dscalar_t, DIM> b{};
|
||||
b(0) = cos(x[0] * 2.0 * M_PI);
|
||||
b(1) = 1.0 + cos(x[1] * 2.0 * M_PI);
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
b(2) = 2.0 + cos(x[2] * 2.0 * M_PI);
|
||||
}
|
||||
return tuple{dot(b, dudx) * w * det(J)};
|
||||
};
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, SCALAR> {};
|
||||
dop.AddDomainIntegrator(convection_qf,
|
||||
tuple{Gradient<SCALAR>{}, Value<COORDINATES>{}, Gradient<COORDINATES>{}, Weight{}},
|
||||
tuple{Value<SCALAR>{}},
|
||||
*ir, all_domain_attr, derivatives);
|
||||
dop.SetParameters({nodes});
|
||||
|
||||
Vector S, T, U;
|
||||
S.SetSize(scalar_fes.GetTrueVSize());
|
||||
T.SetSize(scalar_fes.GetTrueVSize());
|
||||
U.SetSize(scalar_fes.GetTrueVSize());
|
||||
U.Randomize(1);
|
||||
|
||||
{
|
||||
Gmat->MultTranspose(U, S);
|
||||
|
||||
auto ddop = dop.GetDerivative(SCALAR, {&sgf}, {nodes});
|
||||
ddop->MultTranspose(U, T);
|
||||
|
||||
S -= T;
|
||||
real_t norm_g, norm_l = S.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
delete Gmat;
|
||||
}
|
||||
|
||||
SECTION("Nonlinear VectorConvection Transpose Action")
|
||||
{
|
||||
auto b_func = [](const Vector &x, Vector &b)
|
||||
{
|
||||
b(0) = cos(x[0]) * sin(x[0]) * x[1];
|
||||
b(1) = cos(x[1]) * sin(x[1]) * x[0];
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
b(2) = cos(x[2]) * sin(x[2]) * x[0];
|
||||
}
|
||||
};
|
||||
VectorFunctionCoefficient b_coeff(DIM, b_func);
|
||||
|
||||
ParGridFunction ugf(&vector_fes);
|
||||
ugf.ProjectCoefficient(b_coeff);
|
||||
|
||||
Vector U(vector_fes.GetTrueVSize());
|
||||
ugf.GetTrueDofs(U);
|
||||
|
||||
ParNonlinearForm nlf(&vector_fes);
|
||||
const auto vcinteg = new VectorConvectionNLFIntegrator();
|
||||
vcinteg->SetIntegrationRule(*ir);
|
||||
nlf.AddDomainIntegrator(vcinteg);
|
||||
HypreParMatrix &Nmat = dynamic_cast<HypreParMatrix&>(nlf.GetGradient(U));
|
||||
|
||||
static constexpr int VELOCITY = 0, COORDINATES = 1;
|
||||
const auto sol = std::vector{FieldDescriptor{VELOCITY, &vector_fes}};
|
||||
const auto par = std::vector
|
||||
{
|
||||
FieldDescriptor{COORDINATES, nodes->ParFESpace()}
|
||||
};
|
||||
DifferentiableOperator dop(sol, par, mesh);
|
||||
|
||||
const auto nlconvection_qf =
|
||||
[] MFEM_HOST_DEVICE(
|
||||
const tensor<dscalar_t, DIM> &u,
|
||||
const tensor<dscalar_t, DIM, DIM> &dudxi,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
const auto invJ = inv(J);
|
||||
const auto dudx = dudxi * invJ;
|
||||
return tuple{dot(dudx, u) * w * det(J)};
|
||||
};
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, VELOCITY> {};
|
||||
dop.AddDomainIntegrator(nlconvection_qf,
|
||||
tuple{Value<VELOCITY>{}, Gradient<VELOCITY>{}, Gradient<COORDINATES>{}, Weight{}},
|
||||
tuple{Value<VELOCITY>{}},
|
||||
*ir, all_domain_attr, derivatives);
|
||||
dop.SetParameters({nodes});
|
||||
|
||||
auto ddop = dop.GetDerivative(VELOCITY, {&ugf}, {nodes});
|
||||
|
||||
Vector S(U.Size()), T(U.Size()), Se(vector_fes.GetVSize());
|
||||
|
||||
Nmat.MultTranspose(U, S);
|
||||
ddop->MultTranspose(U, T);
|
||||
|
||||
S -= T;
|
||||
real_t norm_g, norm_l = S.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("dFEM Transpose", "[Parallel][dFEM][XXX]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
|
||||
|
||||
SECTION("2d")
|
||||
{
|
||||
const auto filename2d =
|
||||
GENERATE(
|
||||
"../../data/star.mesh",
|
||||
"../../data/star-q3.mesh",
|
||||
"../../data/rt-2d-q3.mesh",
|
||||
"../../data/inline-quad.mesh"
|
||||
);
|
||||
transpose<2>(filename2d, p);
|
||||
}
|
||||
|
||||
SECTION("3d")
|
||||
{
|
||||
const auto filename3d =
|
||||
GENERATE(
|
||||
"../../data/fichera.mesh",
|
||||
"../../data/fichera-q3.mesh",
|
||||
"../../data/inline-hex.mesh",
|
||||
"../../data/toroid-hex.mesh"
|
||||
);
|
||||
transpose<3>(filename3d, p);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -151,6 +151,8 @@ TEST_CASE("Collocated Derivative Kernels", "[QuadratureInterpolator]")
|
||||
auto L = GENERATE(QVectorLayout::byNODES, QVectorLayout::byVDIM);
|
||||
auto P = GENERATE(true, false);
|
||||
|
||||
CAPTURE(L, P);
|
||||
|
||||
const int nd = maps.ndof;
|
||||
const int nq = maps.nqpt;
|
||||
|
||||
@@ -163,7 +165,15 @@ TEST_CASE("Collocated Derivative Kernels", "[QuadratureInterpolator]")
|
||||
CGK::Run(dim, L, P, vdim, nd, nelem, maps.G.Read(), geom->J.Read(),
|
||||
evec_values.Read(), col_der.Write(), sdim, vdim, nd);
|
||||
|
||||
const real_t max_norm = qp_der.Normlinf();
|
||||
|
||||
qp_der -= col_der;
|
||||
REQUIRE(qp_der.Normlinf() == MFEM_Approx(0.0, 1e-10, 1e-10));
|
||||
|
||||
const real_t abs_err = qp_der.Normlinf();
|
||||
const real_t rel_err = max_norm > 0_r ?
|
||||
abs_err/max_norm :
|
||||
abs_err > 0_r ? mfem::infinity() : 0_r;
|
||||
CAPTURE(rel_err, max_norm);
|
||||
CHECK(rel_err <= 1e-13);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,59 @@ TEST_CASE("Array stl-interactions", "[Array]")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Array move assignment to view (MakeRef)", "[Array]")
|
||||
{
|
||||
constexpr int n = 5;
|
||||
|
||||
// Helper function that returns an Array by value (rvalue)
|
||||
auto make_sequence_array = [](int n)
|
||||
{
|
||||
Array<int> arr(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arr[i] = i + 1; // 1, 2, 3, ...
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// Create backing storage
|
||||
Array<int> backing1(n), backing2(n);
|
||||
backing1 = 0.0; backing2 = 0.0;
|
||||
|
||||
// Create a view into the backing storage
|
||||
Array<int> view1, view2;
|
||||
view1.MakeRef(backing1);
|
||||
view2.MakeRef(backing2);
|
||||
|
||||
auto seq_array = make_sequence_array(n);
|
||||
view1 = seq_array; // copy assign
|
||||
view2 = std::move(seq_array); // move assign
|
||||
|
||||
CHECK(seq_array.Size() == 0); // seq_array is invalidated by the move
|
||||
|
||||
// Both assignments should be semantically equivalent.
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
CHECK(backing1[i] == backing2[i]);
|
||||
}
|
||||
|
||||
// Create backing storage as raw array
|
||||
int backing3[n] = {0, 0, 0, 0, 0};
|
||||
|
||||
// Create a view into the backing storage
|
||||
Array<int> view3;
|
||||
view3.MakeRef(backing3, n);
|
||||
|
||||
// Assign from rvalue
|
||||
view3 = std::move(view2);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
CHECK(backing3[i] == backing1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Array delete at indices", "[Array],[GPU]")
|
||||
{
|
||||
for (int use_dev = 0; use_dev < 2; use_dev++)
|
||||
|
||||
@@ -961,3 +961,38 @@ TEST_CASE("NNLS", "[DenseMatrix]")
|
||||
}
|
||||
|
||||
#endif // if MFEM_USE_LAPACK
|
||||
|
||||
TEST_CASE("DenseTensor slice copy and move assign equivalency",
|
||||
"[DenseMatrix][DenseTensor]")
|
||||
{
|
||||
auto fill_matrix = [](int n)
|
||||
{
|
||||
DenseMatrix M(n, n);
|
||||
M = 0.0;
|
||||
for (int i = 0; i < n; i++)
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
M(i, j) = i+j*n+1;
|
||||
}
|
||||
return M;
|
||||
};
|
||||
|
||||
constexpr int n = 3;
|
||||
constexpr int k = 2;
|
||||
|
||||
DenseTensor tensor1(n, n, k), tensor2(n, n, k);
|
||||
tensor1 = 0.0; tensor2 = 0.0;
|
||||
|
||||
DenseMatrix temp = fill_matrix(n);
|
||||
tensor1(0) = temp; // copy assign
|
||||
tensor2(0) = std::move(temp); // move assign
|
||||
|
||||
// Check that the tensor was actually updated
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
CHECK(tensor1(i, j, 0) == tensor2(i, j, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,13 +222,13 @@ TEST_CASE("Greville, Botella and Demko points", "[NURBS]")
|
||||
mfem::out<<"Knotvector : "; kvp.Print(mfem::out);
|
||||
|
||||
kvp.GetGreville(xi);
|
||||
mfem::out<<"Greville points : "; xi.Print(std::cout,999);
|
||||
mfem::out<<"Greville points : "; xi.Print(mfem::out,999);
|
||||
|
||||
kvp.GetBotella(xi);
|
||||
mfem::out<<"Botella points : "; xi.Print(std::cout,999);
|
||||
mfem::out<<"Botella points : "; xi.Print(mfem::out,999);
|
||||
|
||||
kvp.GetDemko(xi);
|
||||
mfem::out<<"Demko points : "; xi.Print(std::cout,999);
|
||||
mfem::out<<"Demko points : "; xi.Print(mfem::out,999);
|
||||
}
|
||||
|
||||
KnotVector kv(3, Vector({0.0, 0.3, 0.3, 0.3, 0.6, 1.0}));
|
||||
|
||||
Reference in New Issue
Block a user