Actual source code: dmproject.c


  2: #include <petsc/private/dmimpl.h>
  3: #include <petscdm.h>
  4: #include <petscdmplex.h>
  5: #include <petscksp.h>
  6: #include <petscblaslapack.h>

  8: typedef struct _projectConstraintsCtx {
  9:   DM  dm;
 10:   Vec mask;
 11: } projectConstraintsCtx;

 13: PetscErrorCode MatMult_GlobalToLocalNormal(Mat CtC, Vec x, Vec y)
 14: {
 15:   DM                     dm;
 16:   Vec                    local, mask;
 17:   projectConstraintsCtx *ctx;

 19:   PetscFunctionBegin;
 20:   PetscCall(MatShellGetContext(CtC, &ctx));
 21:   dm   = ctx->dm;
 22:   mask = ctx->mask;
 23:   PetscCall(DMGetLocalVector(dm, &local));
 24:   PetscCall(DMGlobalToLocalBegin(dm, x, INSERT_VALUES, local));
 25:   PetscCall(DMGlobalToLocalEnd(dm, x, INSERT_VALUES, local));
 26:   if (mask) PetscCall(VecPointwiseMult(local, mask, local));
 27:   PetscCall(VecSet(y, 0.));
 28:   PetscCall(DMLocalToGlobalBegin(dm, local, ADD_VALUES, y));
 29:   PetscCall(DMLocalToGlobalEnd(dm, local, ADD_VALUES, y));
 30:   PetscCall(DMRestoreLocalVector(dm, &local));
 31:   PetscFunctionReturn(PETSC_SUCCESS);
 32: }

 34: static PetscErrorCode DMGlobalToLocalSolve_project1(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nf, PetscScalar u[], void *ctx)
 35: {
 36:   PetscInt f;

 38:   PetscFunctionBegin;
 39:   for (f = 0; f < Nf; f++) u[f] = 1.;
 40:   PetscFunctionReturn(PETSC_SUCCESS);
 41: }

 43: /*@
 44:   DMGlobalToLocalSolve - Solve for the global vector that is mapped to a given local vector by `DMGlobalToLocalBegin()`/`DMGlobalToLocalEnd()` with mode
 45:   = `INSERT_VALUES`.  It is assumed that the sum of all the local vector sizes is greater than or equal to the global vector size, so the solution is
 46:   a least-squares solution.  It is also assumed that `DMLocalToGlobalBegin()`/`DMLocalToGlobalEnd()` with mode = `ADD_VALUES` is the adjoint of the
 47:   global-to-local map, so that the least-squares solution may be found by the normal equations.

 49:   collective

 51:   Input Parameters:
 52: + dm - The `DM` object
 53: . x - The local vector
 54: - y - The global vector: the input value of globalVec is used as an initial guess

 56:   Output Parameters:
 57: . y - The least-squares solution

 59:   Level: advanced

 61:   Note:
 62:   If the `DM` is of type `DMPLEX`, then y is the solution of L' * D * L * y = L' * D * x, where D is a diagonal mask that is 1 for every point in
 63:   the union of the closures of the local cells and 0 otherwise.  This difference is only relevant if there are anchor points that are not in the
 64:   closure of any local cell (see `DMPlexGetAnchors()`/`DMPlexSetAnchors()`).

 66: .seealso: [](chapter_ksp), `DM`, `DMGlobalToLocalBegin()`, `DMGlobalToLocalEnd()`, `DMLocalToGlobalBegin()`, `DMLocalToGlobalEnd()`, `DMPlexGetAnchors()`, `DMPlexSetAnchors()`
 67: @*/
 68: PetscErrorCode DMGlobalToLocalSolve(DM dm, Vec x, Vec y)
 69: {
 70:   Mat                   CtC;
 71:   PetscInt              n, N, cStart, cEnd, c;
 72:   PetscBool             isPlex;
 73:   KSP                   ksp;
 74:   PC                    pc;
 75:   Vec                   global, mask = NULL;
 76:   projectConstraintsCtx ctx;

 78:   PetscFunctionBegin;
 79:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
 80:   if (isPlex) {
 81:     /* mark points in the closure */
 82:     PetscCall(DMCreateLocalVector(dm, &mask));
 83:     PetscCall(VecSet(mask, 0.0));
 84:     PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
 85:     if (cEnd > cStart) {
 86:       PetscScalar *ones;
 87:       PetscInt     numValues, i;

 89:       PetscCall(DMPlexVecGetClosure(dm, NULL, mask, cStart, &numValues, NULL));
 90:       PetscCall(PetscMalloc1(numValues, &ones));
 91:       for (i = 0; i < numValues; i++) ones[i] = 1.;
 92:       for (c = cStart; c < cEnd; c++) PetscCall(DMPlexVecSetClosure(dm, NULL, mask, c, ones, INSERT_VALUES));
 93:       PetscCall(PetscFree(ones));
 94:     }
 95:   } else {
 96:     PetscBool hasMask;

 98:     PetscCall(DMHasNamedLocalVector(dm, "_DMGlobalToLocalSolve_mask", &hasMask));
 99:     if (!hasMask) {
100:       PetscErrorCode (**func)(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nf, PetscScalar *u, void *ctx);
101:       void   **ctx;
102:       PetscInt Nf, f;

104:       PetscCall(DMGetNumFields(dm, &Nf));
105:       PetscCall(PetscMalloc2(Nf, &func, Nf, &ctx));
106:       for (f = 0; f < Nf; ++f) {
107:         func[f] = DMGlobalToLocalSolve_project1;
108:         ctx[f]  = NULL;
109:       }
110:       PetscCall(DMGetNamedLocalVector(dm, "_DMGlobalToLocalSolve_mask", &mask));
111:       PetscCall(DMProjectFunctionLocal(dm, 0.0, func, ctx, INSERT_ALL_VALUES, mask));
112:       PetscCall(DMRestoreNamedLocalVector(dm, "_DMGlobalToLocalSolve_mask", &mask));
113:       PetscCall(PetscFree2(func, ctx));
114:     }
115:     PetscCall(DMGetNamedLocalVector(dm, "_DMGlobalToLocalSolve_mask", &mask));
116:   }
117:   ctx.dm   = dm;
118:   ctx.mask = mask;
119:   PetscCall(VecGetSize(y, &N));
120:   PetscCall(VecGetLocalSize(y, &n));
121:   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), &CtC));
122:   PetscCall(MatSetSizes(CtC, n, n, N, N));
123:   PetscCall(MatSetType(CtC, MATSHELL));
124:   PetscCall(MatSetUp(CtC));
125:   PetscCall(MatShellSetContext(CtC, &ctx));
126:   PetscCall(MatShellSetOperation(CtC, MATOP_MULT, (void (*)(void))MatMult_GlobalToLocalNormal));
127:   PetscCall(KSPCreate(PetscObjectComm((PetscObject)dm), &ksp));
128:   PetscCall(KSPSetOperators(ksp, CtC, CtC));
129:   PetscCall(KSPSetType(ksp, KSPCG));
130:   PetscCall(KSPGetPC(ksp, &pc));
131:   PetscCall(PCSetType(pc, PCNONE));
132:   PetscCall(KSPSetInitialGuessNonzero(ksp, PETSC_TRUE));
133:   PetscCall(KSPSetUp(ksp));
134:   PetscCall(DMGetGlobalVector(dm, &global));
135:   PetscCall(VecSet(global, 0.));
136:   if (mask) PetscCall(VecPointwiseMult(x, mask, x));
137:   PetscCall(DMLocalToGlobalBegin(dm, x, ADD_VALUES, global));
138:   PetscCall(DMLocalToGlobalEnd(dm, x, ADD_VALUES, global));
139:   PetscCall(KSPSolve(ksp, global, y));
140:   PetscCall(DMRestoreGlobalVector(dm, &global));
141:   /* clean up */
142:   PetscCall(KSPDestroy(&ksp));
143:   PetscCall(MatDestroy(&CtC));
144:   if (isPlex) {
145:     PetscCall(VecDestroy(&mask));
146:   } else {
147:     PetscCall(DMRestoreNamedLocalVector(dm, "_DMGlobalToLocalSolve_mask", &mask));
148:   }

150:   PetscFunctionReturn(PETSC_SUCCESS);
151: }

153: /*@C
154:   DMProjectField - This projects the given function of the input fields into the function space provided, putting the coefficients in a global vector.

156:   Collective on dm

158:   Input Parameters:
159: + dm      - The `DM`
160: . time    - The time
161: . U       - The input field vector
162: . funcs   - The functions to evaluate, one per field
163: - mode    - The insertion mode for values

165:   Output Parameter:
166: . X       - The output vector

168:    Calling sequence of func:
169: .vb
170:     func(PetscInt dim, PetscInt Nf, PetscInt NfAux,
171:          const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
172:          const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
173:          PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]);
174: .ve

176: +  dim          - The spatial dimension
177: .  Nf           - The number of input fields
178: .  NfAux        - The number of input auxiliary fields
179: .  uOff         - The offset of each field in u[]
180: .  uOff_x       - The offset of each field in u_x[]
181: .  u            - The field values at this point in space
182: .  u_t          - The field time derivative at this point in space (or NULL)
183: .  u_x          - The field derivatives at this point in space
184: .  aOff         - The offset of each auxiliary field in u[]
185: .  aOff_x       - The offset of each auxiliary field in u_x[]
186: .  a            - The auxiliary field values at this point in space
187: .  a_t          - The auxiliary field time derivative at this point in space (or NULL)
188: .  a_x          - The auxiliary field derivatives at this point in space
189: .  t            - The current time
190: .  x            - The coordinates of this point
191: .  numConstants - The number of constants
192: .  constants    - The value of each constant
193: -  f            - The value of the function at this point in space

195:   Level: advanced

197:   Note:
198:   There are three different `DM`s that potentially interact in this function. The output `DM`, dm, specifies the layout of the values calculates by funcs.
199:   The input `DM`, attached to U, may be different. For example, you can input the solution over the full domain, but output over a piece of the boundary, or
200:   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
201:   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.

203: .seealso: [](chapter_ksp), `DM`, `DMProjectFieldLocal()`, `DMProjectFieldLabelLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`
204: @*/
205: PetscErrorCode DMProjectField(DM dm, PetscReal time, Vec U, void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), InsertMode mode, Vec X)
206: {
207:   Vec localX, localU;
208:   DM  dmIn;

210:   PetscFunctionBegin;
212:   PetscCall(DMGetLocalVector(dm, &localX));
213:   /* We currently check whether locU == locX to see if we need to apply BC */
214:   if (U != X) {
215:     PetscCall(VecGetDM(U, &dmIn));
216:     PetscCall(DMGetLocalVector(dmIn, &localU));
217:   } else {
218:     dmIn   = dm;
219:     localU = localX;
220:   }
221:   PetscCall(DMGlobalToLocalBegin(dmIn, U, INSERT_VALUES, localU));
222:   PetscCall(DMGlobalToLocalEnd(dmIn, U, INSERT_VALUES, localU));
223:   PetscCall(DMProjectFieldLocal(dm, time, localU, funcs, mode, localX));
224:   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
225:   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
226:   if (mode == INSERT_VALUES || mode == INSERT_ALL_VALUES || mode == INSERT_BC_VALUES) {
227:     Mat cMat;

229:     PetscCall(DMGetDefaultConstraints(dm, NULL, &cMat, NULL));
230:     if (cMat) PetscCall(DMGlobalToLocalSolve(dm, localX, X));
231:   }
232:   PetscCall(DMRestoreLocalVector(dm, &localX));
233:   if (U != X) PetscCall(DMRestoreLocalVector(dmIn, &localU));
234:   PetscFunctionReturn(PETSC_SUCCESS);
235: }

237: /********************* Adaptive Interpolation **************************/

239: /* See the discussion of Adaptive Interpolation in manual/high_level_mg.rst */
240: PetscErrorCode DMAdaptInterpolator(DM dmc, DM dmf, Mat In, KSP smoother, Mat MF, Mat MC, Mat *InAdapt, void *user)
241: {
242:   Mat                globalA, AF;
243:   Vec                tmp;
244:   const PetscScalar *af, *ac;
245:   PetscScalar       *A, *b, *x, *workscalar;
246:   PetscReal         *w, *sing, *workreal, rcond = PETSC_SMALL;
247:   PetscBLASInt       M, N, one = 1, irank, lwrk, info;
248:   PetscInt           debug = 0, rStart, rEnd, r, maxcols = 0, k, Nc, ldac, ldaf;
249:   PetscBool          allocVc = PETSC_FALSE;

251:   PetscFunctionBegin;
252:   PetscCall(PetscLogEventBegin(DM_AdaptInterpolator, dmc, dmf, 0, 0));
253:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-dm_interpolator_adapt_debug", &debug, NULL));
254:   PetscCall(MatGetSize(MF, NULL, &Nc));
255:   PetscCall(MatDuplicate(In, MAT_SHARE_NONZERO_PATTERN, InAdapt));
256:   PetscCall(MatGetOwnershipRange(In, &rStart, &rEnd));
257: #if 0
258:   PetscCall(MatGetMaxRowLen(In, &maxcols));
259: #else
260:   for (r = rStart; r < rEnd; ++r) {
261:     PetscInt ncols;

263:     PetscCall(MatGetRow(In, r, &ncols, NULL, NULL));
264:     maxcols = PetscMax(maxcols, ncols);
265:     PetscCall(MatRestoreRow(In, r, &ncols, NULL, NULL));
266:   }
267: #endif
268:   if (Nc < maxcols) PetscCall(PetscPrintf(PETSC_COMM_SELF, "The number of input vectors %" PetscInt_FMT " < %" PetscInt_FMT " the maximum number of column entries\n", Nc, maxcols));
269:   for (k = 0; k < Nc && debug; ++k) {
270:     char        name[PETSC_MAX_PATH_LEN];
271:     const char *prefix;
272:     Vec         vc, vf;

274:     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)smoother, &prefix));

276:     if (MC) {
277:       PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN, "%sCoarse Vector %" PetscInt_FMT, prefix ? prefix : NULL, k));
278:       PetscCall(MatDenseGetColumnVecRead(MC, k, &vc));
279:       PetscCall(PetscObjectSetName((PetscObject)vc, name));
280:       PetscCall(VecViewFromOptions(vc, NULL, "-dm_adapt_interp_view_coarse"));
281:       PetscCall(MatDenseRestoreColumnVecRead(MC, k, &vc));
282:     }
283:     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN, "%sFine Vector %" PetscInt_FMT, prefix ? prefix : NULL, k));
284:     PetscCall(MatDenseGetColumnVecRead(MF, k, &vf));
285:     PetscCall(PetscObjectSetName((PetscObject)vf, name));
286:     PetscCall(VecViewFromOptions(vf, NULL, "-dm_adapt_interp_view_fine"));
287:     PetscCall(MatDenseRestoreColumnVecRead(MF, k, &vf));
288:   }
289:   PetscCall(PetscBLASIntCast(3 * PetscMin(Nc, maxcols) + PetscMax(2 * PetscMin(Nc, maxcols), PetscMax(Nc, maxcols)), &lwrk));
290:   PetscCall(PetscMalloc7(Nc * maxcols, &A, PetscMax(Nc, maxcols), &b, Nc, &w, maxcols, &x, maxcols, &sing, lwrk, &workscalar, 5 * PetscMin(Nc, maxcols), &workreal));
291:   /* w_k = \frac{\HC{v_k} B_l v_k}{\HC{v_k} A_l v_k} or the inverse Rayleigh quotient, which we calculate using \frac{\HC{v_k} v_k}{\HC{v_k} B^{-1}_l A_l v_k} */
292:   PetscCall(KSPGetOperators(smoother, &globalA, NULL));

294:   PetscCall(MatMatMult(globalA, MF, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &AF));
295:   for (k = 0; k < Nc; ++k) {
296:     PetscScalar vnorm, vAnorm;
297:     Vec         vf;

299:     w[k] = 1.0;
300:     PetscCall(MatDenseGetColumnVecRead(MF, k, &vf));
301:     PetscCall(MatDenseGetColumnVecRead(AF, k, &tmp));
302:     PetscCall(VecDot(vf, vf, &vnorm));
303: #if 0
304:     PetscCall(DMGetGlobalVector(dmf, &tmp2));
305:     PetscCall(KSPSolve(smoother, tmp, tmp2));
306:     PetscCall(VecDot(vf, tmp2, &vAnorm));
307:     PetscCall(DMRestoreGlobalVector(dmf, &tmp2));
308: #else
309:     PetscCall(VecDot(vf, tmp, &vAnorm));
310: #endif
311:     w[k] = PetscRealPart(vnorm) / PetscRealPart(vAnorm);
312:     PetscCall(MatDenseRestoreColumnVecRead(MF, k, &vf));
313:     PetscCall(MatDenseRestoreColumnVecRead(AF, k, &tmp));
314:   }
315:   PetscCall(MatDestroy(&AF));
316:   if (!MC) {
317:     allocVc = PETSC_TRUE;
318:     PetscCall(MatTransposeMatMult(In, MF, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &MC));
319:   }
320:   /* Solve a LS system for each fine row
321:      MATT: Can we generalize to the case where Nc for the fine space
322:      is different for Nc for the coarse? */
323:   PetscCall(MatDenseGetArrayRead(MF, &af));
324:   PetscCall(MatDenseGetLDA(MF, &ldaf));
325:   PetscCall(MatDenseGetArrayRead(MC, &ac));
326:   PetscCall(MatDenseGetLDA(MC, &ldac));
327:   for (r = rStart; r < rEnd; ++r) {
328:     PetscInt           ncols, c;
329:     const PetscInt    *cols;
330:     const PetscScalar *vals;

332:     PetscCall(MatGetRow(In, r, &ncols, &cols, &vals));
333:     for (k = 0; k < Nc; ++k) {
334:       /* Need to fit lowest mode exactly */
335:       const PetscReal wk = ((ncols == 1) && (k > 0)) ? 0.0 : PetscSqrtReal(w[k]);

337:       /* b_k = \sqrt{w_k} f^{F,k}_r */
338:       b[k] = wk * af[r - rStart + k * ldaf];
339:       /* A_{kc} = \sqrt{w_k} f^{C,k}_c */
340:       /* TODO Must pull out VecScatter from In, scatter in vc[k] values up front, and access them indirectly just as in MatMult() */
341:       for (c = 0; c < ncols; ++c) {
342:         /* This is element (k, c) of A */
343:         A[c * Nc + k] = wk * ac[cols[c] - rStart + k * ldac];
344:       }
345:     }
346:     PetscCall(PetscBLASIntCast(Nc, &M));
347:     PetscCall(PetscBLASIntCast(ncols, &N));
348:     if (debug) {
349: #if defined(PETSC_USE_COMPLEX)
350:       PetscScalar *tmp;
351:       PetscInt     j;

353:       PetscCall(DMGetWorkArray(dmc, Nc, MPIU_SCALAR, (void *)&tmp));
354:       for (j = 0; j < Nc; ++j) tmp[j] = w[j];
355:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS weights", Nc, 1, tmp));
356:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS matrix", Nc, ncols, A));
357:       for (j = 0; j < Nc; ++j) tmp[j] = b[j];
358:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS rhs", Nc, 1, tmp));
359:       PetscCall(DMRestoreWorkArray(dmc, Nc, MPIU_SCALAR, (void *)&tmp));
360: #else
361:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS weights", Nc, 1, w));
362:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS matrix", Nc, ncols, A));
363:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS rhs", Nc, 1, b));
364: #endif
365:     }
366: #if defined(PETSC_USE_COMPLEX)
367:     /* ZGELSS( M, N, NRHS, A, LDA, B, LDB, S, RCOND, RANK, WORK, LWORK, RWORK, INFO) */
368:     PetscCallBLAS("LAPACKgelss", LAPACKgelss_(&M, &N, &one, A, &M, b, M > N ? &M : &N, sing, &rcond, &irank, workscalar, &lwrk, workreal, &info));
369: #else
370:     /* DGELSS( M, N, NRHS, A, LDA, B, LDB, S, RCOND, RANK, WORK, LWORK, INFO) */
371:     PetscCallBLAS("LAPACKgelss", LAPACKgelss_(&M, &N, &one, A, &M, b, M > N ? &M : &N, sing, &rcond, &irank, workscalar, &lwrk, &info));
372: #endif
373:     PetscCheck(info >= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "Bad argument to GELSS");
374:     PetscCheck(info <= 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "SVD failed to converge");
375:     if (debug) {
376:       PetscCall(PetscPrintf(PETSC_COMM_SELF, "rank %" PetscBLASInt_FMT " rcond %g\n", irank, (double)rcond));
377: #if defined(PETSC_USE_COMPLEX)
378:       {
379:         PetscScalar *tmp;
380:         PetscInt     j;

382:         PetscCall(DMGetWorkArray(dmc, Nc, MPIU_SCALAR, (void *)&tmp));
383:         for (j = 0; j < PetscMin(Nc, ncols); ++j) tmp[j] = sing[j];
384:         PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS singular values", PetscMin(Nc, ncols), 1, tmp));
385:         PetscCall(DMRestoreWorkArray(dmc, Nc, MPIU_SCALAR, (void *)&tmp));
386:       }
387: #else
388:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS singular values", PetscMin(Nc, ncols), 1, sing));
389: #endif
390:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS old P", ncols, 1, vals));
391:       PetscCall(DMPrintCellMatrix(r, "Interpolator Row LS sol", ncols, 1, b));
392:     }
393:     PetscCall(MatSetValues(*InAdapt, 1, &r, ncols, cols, b, INSERT_VALUES));
394:     PetscCall(MatRestoreRow(In, r, &ncols, &cols, &vals));
395:   }
396:   PetscCall(MatDenseRestoreArrayRead(MF, &af));
397:   PetscCall(MatDenseRestoreArrayRead(MC, &ac));
398:   PetscCall(PetscFree7(A, b, w, x, sing, workscalar, workreal));
399:   if (allocVc) PetscCall(MatDestroy(&MC));
400:   PetscCall(MatAssemblyBegin(*InAdapt, MAT_FINAL_ASSEMBLY));
401:   PetscCall(MatAssemblyEnd(*InAdapt, MAT_FINAL_ASSEMBLY));
402:   PetscCall(PetscLogEventEnd(DM_AdaptInterpolator, dmc, dmf, 0, 0));
403:   PetscFunctionReturn(PETSC_SUCCESS);
404: }

406: PetscErrorCode DMCheckInterpolator(DM dmf, Mat In, Mat MC, Mat MF, PetscReal tol)
407: {
408:   Vec       tmp;
409:   PetscReal norminf, norm2, maxnorminf = 0.0, maxnorm2 = 0.0;
410:   PetscInt  k, Nc;

412:   PetscFunctionBegin;
413:   PetscCall(DMGetGlobalVector(dmf, &tmp));
414:   PetscCall(MatViewFromOptions(In, NULL, "-dm_interpolator_adapt_error"));
415:   PetscCall(MatGetSize(MF, NULL, &Nc));
416:   for (k = 0; k < Nc; ++k) {
417:     Vec vc, vf;

419:     PetscCall(MatDenseGetColumnVecRead(MC, k, &vc));
420:     PetscCall(MatDenseGetColumnVecRead(MF, k, &vf));
421:     PetscCall(MatMult(In, vc, tmp));
422:     PetscCall(VecAXPY(tmp, -1.0, vf));
423:     PetscCall(VecViewFromOptions(vc, NULL, "-dm_interpolator_adapt_error"));
424:     PetscCall(VecViewFromOptions(vf, NULL, "-dm_interpolator_adapt_error"));
425:     PetscCall(VecViewFromOptions(tmp, NULL, "-dm_interpolator_adapt_error"));
426:     PetscCall(VecNorm(tmp, NORM_INFINITY, &norminf));
427:     PetscCall(VecNorm(tmp, NORM_2, &norm2));
428:     maxnorminf = PetscMax(maxnorminf, norminf);
429:     maxnorm2   = PetscMax(maxnorm2, norm2);
430:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dmf), "Coarse vec %" PetscInt_FMT " ||vf - P vc||_\\infty %g, ||vf - P vc||_2 %g\n", k, (double)norminf, (double)norm2));
431:     PetscCall(MatDenseRestoreColumnVecRead(MC, k, &vc));
432:     PetscCall(MatDenseRestoreColumnVecRead(MF, k, &vf));
433:   }
434:   PetscCall(DMRestoreGlobalVector(dmf, &tmp));
435:   PetscCheck(maxnorm2 <= tol, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_WRONG, "max_k ||vf_k - P vc_k||_2 %g > tol %g", (double)maxnorm2, (double)tol);
436:   PetscFunctionReturn(PETSC_SUCCESS);
437: }