Actual source code: plexglvis.c

  1: #include <petsc/private/glvisviewerimpl.h>
  2: #include <petsc/private/petscimpl.h>
  3: #include <petsc/private/dmpleximpl.h>
  4: #include <petscbt.h>
  5: #include <petscdmplex.h>
  6: #include <petscsf.h>
  7: #include <petscds.h>

  9: typedef struct {
 10:   PetscInt    nf;
 11:   VecScatter *scctx;
 12: } GLVisViewerCtx;

 14: static PetscErrorCode DestroyGLVisViewerCtx_Private(PetscCtxRt vctx)
 15: {
 16:   GLVisViewerCtx *ctx = *(GLVisViewerCtx **)vctx;
 17:   PetscInt        i;

 19:   PetscFunctionBegin;
 20:   for (i = 0; i < ctx->nf; i++) PetscCall(VecScatterDestroy(&ctx->scctx[i]));
 21:   PetscCall(PetscFree(ctx->scctx));
 22:   PetscCall(PetscFree(ctx));
 23:   PetscFunctionReturn(PETSC_SUCCESS);
 24: }

 26: static PetscErrorCode DMPlexSampleGLVisFields_Private(PetscObject oX, PetscInt nf, PetscObject oXfield[], void *vctx)
 27: {
 28:   GLVisViewerCtx *ctx = (GLVisViewerCtx *)vctx;
 29:   PetscInt        f;

 31:   PetscFunctionBegin;
 32:   for (f = 0; f < nf; f++) {
 33:     PetscCall(VecScatterBegin(ctx->scctx[f], (Vec)oX, (Vec)oXfield[f], INSERT_VALUES, SCATTER_FORWARD));
 34:     PetscCall(VecScatterEnd(ctx->scctx[f], (Vec)oX, (Vec)oXfield[f], INSERT_VALUES, SCATTER_FORWARD));
 35:   }
 36:   PetscFunctionReturn(PETSC_SUCCESS);
 37: }

 39: /* for FEM, it works for H1 fields only and extracts dofs at cell vertices, discarding any other dof */
 40: PetscErrorCode DMSetUpGLVisViewer_Plex(PetscObject odm, PetscViewer viewer)
 41: {
 42:   DM              dm = (DM)odm;
 43:   Vec             xlocal, xfield, *Ufield;
 44:   PetscDS         ds;
 45:   IS              globalNum, isfield;
 46:   PetscBT         vown;
 47:   char          **fieldname = NULL, **fec_type = NULL;
 48:   const PetscInt *gNum;
 49:   PetscInt       *nlocal, *bs, *idxs, *dims;
 50:   PetscInt        f, maxfields, nfields, c, totc, totdofs, Nv, cum, i;
 51:   PetscInt        dim, cStart, cEnd, vStart, vEnd;
 52:   GLVisViewerCtx *ctx;
 53:   PetscSection    s;

 55:   PetscFunctionBegin;
 56:   PetscCall(DMGetDimension(dm, &dim));
 57:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
 58:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
 59:   PetscCall(PetscObjectQuery((PetscObject)dm, "_glvis_plex_gnum", (PetscObject *)&globalNum));
 60:   if (!globalNum) {
 61:     PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &globalNum));
 62:     PetscCall(PetscObjectCompose((PetscObject)dm, "_glvis_plex_gnum", (PetscObject)globalNum));
 63:     PetscCall(PetscObjectDereference((PetscObject)globalNum));
 64:   }
 65:   PetscCall(ISGetIndices(globalNum, &gNum));
 66:   PetscCall(PetscBTCreate(vEnd - vStart, &vown));
 67:   for (c = cStart, totc = 0; c < cEnd; c++) {
 68:     if (gNum[c - cStart] >= 0) {
 69:       PetscInt i, numPoints, *points = NULL;

 71:       totc++;
 72:       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &numPoints, &points));
 73:       for (i = 0; i < numPoints * 2; i += 2) {
 74:         if (points[i] >= vStart && points[i] < vEnd) PetscCall(PetscBTSet(vown, points[i] - vStart));
 75:       }
 76:       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &numPoints, &points));
 77:     }
 78:   }
 79:   for (f = 0, Nv = 0; f < vEnd - vStart; f++)
 80:     if (PetscLikely(PetscBTLookup(vown, f))) Nv++;

 82:   PetscCall(DMCreateLocalVector(dm, &xlocal));
 83:   PetscCall(VecGetLocalSize(xlocal, &totdofs));
 84:   PetscCall(DMGetLocalSection(dm, &s));
 85:   PetscCall(PetscSectionGetNumFields(s, &nfields));
 86:   for (f = 0, maxfields = 0; f < nfields; f++) {
 87:     PetscInt bs;

 89:     PetscCall(PetscSectionGetFieldComponents(s, f, &bs));
 90:     maxfields += bs;
 91:   }
 92:   PetscCall(PetscCalloc7(maxfields, &fieldname, maxfields, &nlocal, maxfields, &bs, maxfields, &dims, maxfields, &fec_type, totdofs, &idxs, maxfields, &Ufield));
 93:   PetscCall(PetscNew(&ctx));
 94:   PetscCall(PetscCalloc1(maxfields, &ctx->scctx));
 95:   PetscCall(DMGetDS(dm, &ds));
 96:   if (ds) {
 97:     for (f = 0; f < nfields; f++) {
 98:       const char *fname;
 99:       char        name[256];
100:       PetscObject disc;
101:       size_t      len;

103:       PetscCall(PetscSectionGetFieldName(s, f, &fname));
104:       PetscCall(PetscStrlen(fname, &len));
105:       if (len) {
106:         PetscCall(PetscStrncpy(name, fname, sizeof(name)));
107:       } else {
108:         PetscCall(PetscSNPrintf(name, 256, "Field%" PetscInt_FMT, f));
109:       }
110:       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
111:       if (disc) {
112:         PetscClassId id;
113:         PetscInt     Nc;
114:         char         fec[64];

116:         PetscCall(PetscObjectGetClassId(disc, &id));
117:         if (id == PETSCFE_CLASSID) {
118:           PetscFE            fem = (PetscFE)disc;
119:           PetscDualSpace     sp;
120:           PetscDualSpaceType spname;
121:           PetscInt           order;
122:           PetscBool          islag, continuous, H1 = PETSC_TRUE;

124:           PetscCall(PetscFEGetNumComponents(fem, &Nc));
125:           PetscCall(PetscFEGetDualSpace(fem, &sp));
126:           PetscCall(PetscDualSpaceGetType(sp, &spname));
127:           PetscCall(PetscStrcmp(spname, PETSCDUALSPACELAGRANGE, &islag));
128:           PetscCheck(islag, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dual space");
129:           PetscCall(PetscDualSpaceLagrangeGetContinuity(sp, &continuous));
130:           PetscCall(PetscDualSpaceGetOrder(sp, &order));
131:           if (continuous && order > 0) { /* no support for high-order viz, still have to figure out the numbering */
132:             PetscCall(PetscSNPrintf(fec, 64, "FiniteElementCollection: H1_%" PetscInt_FMT "D_P1", dim));
133:           } else {
134:             PetscCheck(continuous || !order, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Discontinuous space visualization currently unsupported for order %" PetscInt_FMT, order);
135:             H1 = PETSC_FALSE;
136:             PetscCall(PetscSNPrintf(fec, 64, "FiniteElementCollection: L2_%" PetscInt_FMT "D_P%" PetscInt_FMT, dim, order));
137:           }
138:           PetscCall(PetscStrallocpy(name, &fieldname[ctx->nf]));
139:           bs[ctx->nf]   = Nc;
140:           dims[ctx->nf] = dim;
141:           if (H1) {
142:             nlocal[ctx->nf] = Nc * Nv;
143:             PetscCall(PetscStrallocpy(fec, &fec_type[ctx->nf]));
144:             PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nv * Nc, &xfield));
145:             for (i = 0, cum = 0; i < vEnd - vStart; i++) {
146:               PetscInt off;

148:               if (PetscUnlikely(!PetscBTLookup(vown, i))) continue;
149:               PetscCall(PetscSectionGetFieldOffset(s, i + vStart, f, &off));
150:               for (PetscInt j = 0; j < Nc; j++) idxs[cum++] = off + j;
151:             }
152:             PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)xlocal), Nv * Nc, idxs, PETSC_USE_POINTER, &isfield));
153:           } else {
154:             nlocal[ctx->nf] = Nc * totc;
155:             PetscCall(PetscStrallocpy(fec, &fec_type[ctx->nf]));
156:             PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nc * totc, &xfield));
157:             for (i = 0, cum = 0; i < cEnd - cStart; i++) {
158:               PetscInt off;

160:               if (PetscUnlikely(gNum[i] < 0)) continue;
161:               PetscCall(PetscSectionGetFieldOffset(s, i + cStart, f, &off));
162:               for (PetscInt j = 0; j < Nc; j++) idxs[cum++] = off + j;
163:             }
164:             PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)xlocal), totc * Nc, idxs, PETSC_USE_POINTER, &isfield));
165:           }
166:           PetscCall(VecScatterCreate(xlocal, isfield, xfield, NULL, &ctx->scctx[ctx->nf]));
167:           PetscCall(VecDestroy(&xfield));
168:           PetscCall(ISDestroy(&isfield));
169:           ctx->nf++;
170:         } else if (id == PETSCFV_CLASSID) {
171:           PetscCall(PetscFVGetNumComponents((PetscFV)disc, &Nc));
172:           PetscCall(PetscSNPrintf(fec, 64, "FiniteElementCollection: L2_%" PetscInt_FMT "D_P0", dim));
173:           for (PetscInt c = 0; c < Nc; c++) {
174:             char comp[256];
175:             PetscCall(PetscSNPrintf(comp, 256, "%s-Comp%" PetscInt_FMT, name, c));
176:             PetscCall(PetscStrallocpy(comp, &fieldname[ctx->nf]));
177:             bs[ctx->nf]     = 1; /* Does PetscFV support components with different block size? */
178:             nlocal[ctx->nf] = totc;
179:             dims[ctx->nf]   = dim;
180:             PetscCall(PetscStrallocpy(fec, &fec_type[ctx->nf]));
181:             PetscCall(VecCreateSeq(PETSC_COMM_SELF, totc, &xfield));
182:             for (i = 0, cum = 0; i < cEnd - cStart; i++) {
183:               PetscInt off;

185:               if (PetscUnlikely(gNum[i]) < 0) continue;
186:               PetscCall(PetscSectionGetFieldOffset(s, i + cStart, f, &off));
187:               idxs[cum++] = off + c;
188:             }
189:             PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)xlocal), totc, idxs, PETSC_USE_POINTER, &isfield));
190:             PetscCall(VecScatterCreate(xlocal, isfield, xfield, NULL, &ctx->scctx[ctx->nf]));
191:             PetscCall(VecDestroy(&xfield));
192:             PetscCall(ISDestroy(&isfield));
193:             ctx->nf++;
194:           }
195:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
196:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Missing discretization for field %" PetscInt_FMT, f);
197:     }
198:   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Needs a DS attached to the DM");
199:   PetscCall(PetscBTDestroy(&vown));
200:   PetscCall(VecDestroy(&xlocal));
201:   PetscCall(ISRestoreIndices(globalNum, &gNum));

203:   /* create work vectors */
204:   for (f = 0; f < ctx->nf; f++) {
205:     PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)dm), nlocal[f], PETSC_DECIDE, &Ufield[f]));
206:     PetscCall(PetscObjectSetName((PetscObject)Ufield[f], fieldname[f]));
207:     PetscCall(VecSetBlockSize(Ufield[f], bs[f]));
208:     PetscCall(VecSetDM(Ufield[f], dm));
209:   }

211:   /* customize the viewer */
212:   PetscCall(PetscViewerGLVisSetFields(viewer, ctx->nf, (const char **)fec_type, dims, DMPlexSampleGLVisFields_Private, (PetscObject *)Ufield, ctx, DestroyGLVisViewerCtx_Private));
213:   for (f = 0; f < ctx->nf; f++) {
214:     PetscCall(PetscFree(fieldname[f]));
215:     PetscCall(PetscFree(fec_type[f]));
216:     PetscCall(VecDestroy(&Ufield[f]));
217:   }
218:   PetscCall(PetscFree7(fieldname, nlocal, bs, dims, fec_type, idxs, Ufield));
219:   PetscFunctionReturn(PETSC_SUCCESS);
220: }

222: typedef enum {
223:   MFEM_POINT = 0,
224:   MFEM_SEGMENT,
225:   MFEM_TRIANGLE,
226:   MFEM_SQUARE,
227:   MFEM_TETRAHEDRON,
228:   MFEM_CUBE,
229:   MFEM_PRISM,
230:   MFEM_UNDEF
231: } MFEM_cid;

233: MFEM_cid mfem_table_cid[4][7] = {
234:   {MFEM_POINT, MFEM_UNDEF, MFEM_UNDEF,   MFEM_UNDEF,    MFEM_UNDEF,       MFEM_UNDEF, MFEM_UNDEF},
235:   {MFEM_POINT, MFEM_UNDEF, MFEM_SEGMENT, MFEM_UNDEF,    MFEM_UNDEF,       MFEM_UNDEF, MFEM_UNDEF},
236:   {MFEM_POINT, MFEM_UNDEF, MFEM_SEGMENT, MFEM_TRIANGLE, MFEM_SQUARE,      MFEM_UNDEF, MFEM_UNDEF},
237:   {MFEM_POINT, MFEM_UNDEF, MFEM_SEGMENT, MFEM_UNDEF,    MFEM_TETRAHEDRON, MFEM_PRISM, MFEM_CUBE }
238: };

240: MFEM_cid mfem_table_cid_unint[4][9] = {
241:   {MFEM_POINT, MFEM_UNDEF, MFEM_UNDEF,   MFEM_UNDEF,    MFEM_UNDEF,       MFEM_UNDEF, MFEM_PRISM, MFEM_UNDEF, MFEM_UNDEF},
242:   {MFEM_POINT, MFEM_UNDEF, MFEM_SEGMENT, MFEM_UNDEF,    MFEM_UNDEF,       MFEM_UNDEF, MFEM_PRISM, MFEM_UNDEF, MFEM_UNDEF},
243:   {MFEM_POINT, MFEM_UNDEF, MFEM_SEGMENT, MFEM_TRIANGLE, MFEM_SQUARE,      MFEM_UNDEF, MFEM_PRISM, MFEM_UNDEF, MFEM_UNDEF},
244:   {MFEM_POINT, MFEM_UNDEF, MFEM_SEGMENT, MFEM_UNDEF,    MFEM_TETRAHEDRON, MFEM_UNDEF, MFEM_PRISM, MFEM_UNDEF, MFEM_CUBE }
245: };

247: static PetscErrorCode DMPlexGetPointMFEMCellID_Internal(DM dm, DMLabel label, PetscInt minl, PetscInt p, PetscInt *mid, PetscInt *cid)
248: {
249:   DMLabel  dlabel;
250:   PetscInt depth, csize, pdepth, dim;

252:   PetscFunctionBegin;
253:   PetscCall(DMPlexGetDepthLabel(dm, &dlabel));
254:   PetscCall(DMLabelGetValue(dlabel, p, &pdepth));
255:   PetscCall(DMPlexGetConeSize(dm, p, &csize));
256:   PetscCall(DMPlexGetDepth(dm, &depth));
257:   PetscCall(DMGetDimension(dm, &dim));
258:   if (label) {
259:     PetscCall(DMLabelGetValue(label, p, mid));
260:     *mid = *mid - minl + 1; /* MFEM does not like negative markers */
261:   } else *mid = 1;
262:   if (depth >= 0 && dim != depth) { /* not interpolated, it assumes cell-vertex mesh */
263:     PetscCheck(dim >= 0 && dim <= 3, PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT, dim);
264:     PetscCheck(csize <= 8, PETSC_COMM_SELF, PETSC_ERR_SUP, "Found cone size %" PetscInt_FMT " for point %" PetscInt_FMT, csize, p);
265:     PetscCheck(depth == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Found depth %" PetscInt_FMT " for point %" PetscInt_FMT ". You should interpolate the mesh first", depth, p);
266:     *cid = mfem_table_cid_unint[dim][csize];
267:   } else {
268:     PetscCheck(csize <= 6, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cone size %" PetscInt_FMT " for point %" PetscInt_FMT, csize, p);
269:     PetscCheck(pdepth >= 0 && pdepth <= 3, PETSC_COMM_SELF, PETSC_ERR_SUP, "Depth %" PetscInt_FMT " for point %" PetscInt_FMT, csize, p);
270:     *cid = mfem_table_cid[pdepth][csize];
271:   }
272:   PetscFunctionReturn(PETSC_SUCCESS);
273: }

275: static PetscErrorCode DMPlexGetPointMFEMVertexIDs_Internal(DM dm, PetscInt p, PetscSection csec, PetscInt *nv, PetscInt vids[])
276: {
277:   PetscInt dim, sdim, dof = 0, off = 0, i, q, vStart, vEnd, numPoints, *points = NULL;

279:   PetscFunctionBegin;
280:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
281:   PetscCall(DMGetDimension(dm, &dim));
282:   sdim = dim;
283:   if (csec) {
284:     PetscInt sStart, sEnd;

286:     PetscCall(DMGetCoordinateDim(dm, &sdim));
287:     PetscCall(PetscSectionGetChart(csec, &sStart, &sEnd));
288:     PetscCall(PetscSectionGetOffset(csec, vStart, &off));
289:     off = off / sdim;
290:     if (p >= sStart && p < sEnd) PetscCall(PetscSectionGetDof(csec, p, &dof));
291:   }
292:   if (!dof) {
293:     PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &numPoints, &points));
294:     for (i = 0, q = 0; i < numPoints * 2; i += 2)
295:       if (points[i] >= vStart && points[i] < vEnd) vids[q++] = points[i] - vStart + off;
296:     PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &numPoints, &points));
297:   } else {
298:     PetscCall(PetscSectionGetOffset(csec, p, &off));
299:     PetscCall(PetscSectionGetDof(csec, p, &dof));
300:     for (q = 0; q < dof / sdim; q++) vids[q] = off / sdim + q;
301:   }
302:   *nv = q;
303:   PetscFunctionReturn(PETSC_SUCCESS);
304: }

306: static PetscErrorCode GLVisCreateFE(PetscFE femIn, char name[32], PetscFE *fem, IS *perm)
307: {
308:   DM              K;
309:   PetscSpace      P;
310:   PetscDualSpace  Q;
311:   PetscQuadrature q, fq;
312:   PetscInt        dim, deg, dof;
313:   DMPolytopeType  ptype;
314:   PetscBool       isSimplex, isTensor;
315:   PetscBool       continuity = PETSC_FALSE;
316:   PetscDTNodeType nodeType   = PETSCDTNODES_GAUSSJACOBI;
317:   PetscBool       endpoint   = PETSC_TRUE;
318:   MPI_Comm        comm;

320:   PetscFunctionBegin;
321:   PetscCall(PetscObjectGetComm((PetscObject)femIn, &comm));
322:   PetscCall(PetscFEGetBasisSpace(femIn, &P));
323:   PetscCall(PetscFEGetDualSpace(femIn, &Q));
324:   PetscCall(PetscDualSpaceGetDM(Q, &K));
325:   PetscCall(DMGetDimension(K, &dim));
326:   PetscCall(PetscSpaceGetDegree(P, &deg, NULL));
327:   PetscCall(PetscSpaceGetNumComponents(P, &dof));
328:   PetscCall(DMPlexGetCellType(K, 0, &ptype));
329:   switch (ptype) {
330:   case DM_POLYTOPE_QUADRILATERAL:
331:   case DM_POLYTOPE_HEXAHEDRON:
332:     isSimplex = PETSC_FALSE;
333:     break;
334:   default:
335:     isSimplex = PETSC_TRUE;
336:     break;
337:   }
338:   isTensor = isSimplex ? PETSC_FALSE : PETSC_TRUE;
339:   if (isSimplex) deg = PetscMin(deg, 3); /* Permutation not coded for degree higher than 3 */
340:   /* Create space */
341:   PetscCall(PetscSpaceCreate(comm, &P));
342:   PetscCall(PetscSpaceSetType(P, PETSCSPACEPOLYNOMIAL));
343:   PetscCall(PetscSpacePolynomialSetTensor(P, isTensor));
344:   PetscCall(PetscSpaceSetNumComponents(P, dof));
345:   PetscCall(PetscSpaceSetNumVariables(P, dim));
346:   PetscCall(PetscSpaceSetDegree(P, deg, PETSC_DETERMINE));
347:   PetscCall(PetscSpaceSetUp(P));
348:   /* Create dual space */
349:   PetscCall(PetscDualSpaceCreate(comm, &Q));
350:   PetscCall(PetscDualSpaceSetType(Q, PETSCDUALSPACELAGRANGE));
351:   PetscCall(PetscDualSpaceLagrangeSetTensor(Q, isTensor));
352:   PetscCall(PetscDualSpaceLagrangeSetContinuity(Q, continuity));
353:   PetscCall(PetscDualSpaceLagrangeSetNodeType(Q, nodeType, endpoint, 0));
354:   PetscCall(PetscDualSpaceSetNumComponents(Q, dof));
355:   PetscCall(PetscDualSpaceSetOrder(Q, deg));
356:   PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, DMPolytopeTypeSimpleShape(dim, isSimplex), &K));
357:   PetscCall(PetscDualSpaceSetDM(Q, K));
358:   PetscCall(DMDestroy(&K));
359:   PetscCall(PetscDualSpaceSetUp(Q));
360:   /* Create quadrature */
361:   if (isSimplex) {
362:     PetscCall(PetscDTStroudConicalQuadrature(dim, 1, deg + 1, -1, +1, &q));
363:     PetscCall(PetscDTStroudConicalQuadrature(dim - 1, 1, deg + 1, -1, +1, &fq));
364:   } else {
365:     PetscCall(PetscDTGaussTensorQuadrature(dim, 1, deg + 1, -1, +1, &q));
366:     PetscCall(PetscDTGaussTensorQuadrature(dim - 1, 1, deg + 1, -1, +1, &fq));
367:   }
368:   /* Create finite element */
369:   PetscCall(PetscFECreate(comm, fem));
370:   PetscCall(PetscSNPrintf(name, 32, "L2_T1_%" PetscInt_FMT "D_P%" PetscInt_FMT, dim, deg));
371:   PetscCall(PetscObjectSetName((PetscObject)*fem, name));
372:   PetscCall(PetscFESetType(*fem, PETSCFEBASIC));
373:   PetscCall(PetscFESetNumComponents(*fem, dof));
374:   PetscCall(PetscFESetBasisSpace(*fem, P));
375:   PetscCall(PetscFESetDualSpace(*fem, Q));
376:   PetscCall(PetscFESetQuadrature(*fem, q));
377:   PetscCall(PetscFESetFaceQuadrature(*fem, fq));
378:   PetscCall(PetscFESetUp(*fem));

380:   /* Both MFEM and PETSc are lexicographic, but PLEX stores the swapped cone */
381:   *perm = NULL;
382:   if (isSimplex && dim == 3) {
383:     PetscInt celldofs, *pidx;

385:     PetscCall(PetscDualSpaceGetDimension(Q, &celldofs));
386:     celldofs /= dof;
387:     PetscCall(PetscMalloc1(celldofs, &pidx));
388:     switch (celldofs) {
389:     case 4:
390:       pidx[0] = 2;
391:       pidx[1] = 0;
392:       pidx[2] = 1;
393:       pidx[3] = 3;
394:       break;
395:     case 10:
396:       pidx[0] = 5;
397:       pidx[1] = 3;
398:       pidx[2] = 0;
399:       pidx[3] = 4;
400:       pidx[4] = 1;
401:       pidx[5] = 2;
402:       pidx[6] = 8;
403:       pidx[7] = 6;
404:       pidx[8] = 7;
405:       pidx[9] = 9;
406:       break;
407:     case 20:
408:       pidx[0]  = 9;
409:       pidx[1]  = 7;
410:       pidx[2]  = 4;
411:       pidx[3]  = 0;
412:       pidx[4]  = 8;
413:       pidx[5]  = 5;
414:       pidx[6]  = 1;
415:       pidx[7]  = 6;
416:       pidx[8]  = 2;
417:       pidx[9]  = 3;
418:       pidx[10] = 15;
419:       pidx[11] = 13;
420:       pidx[12] = 10;
421:       pidx[13] = 14;
422:       pidx[14] = 11;
423:       pidx[15] = 12;
424:       pidx[16] = 18;
425:       pidx[17] = 16;
426:       pidx[18] = 17;
427:       pidx[19] = 19;
428:       break;
429:     default:
430:       SETERRQ(comm, PETSC_ERR_SUP, "Unhandled degree,dof pair %" PetscInt_FMT ",%" PetscInt_FMT, deg, celldofs);
431:       break;
432:     }
433:     PetscCall(ISCreateBlock(PETSC_COMM_SELF, dof, celldofs, pidx, PETSC_OWN_POINTER, perm));
434:   }

436:   /* Cleanup */
437:   PetscCall(PetscSpaceDestroy(&P));
438:   PetscCall(PetscDualSpaceDestroy(&Q));
439:   PetscCall(PetscQuadratureDestroy(&q));
440:   PetscCall(PetscQuadratureDestroy(&fq));
441:   PetscFunctionReturn(PETSC_SUCCESS);
442: }

444: /*
445:    ASCII visualization/dump: full support for simplices and tensor product cells. It supports AMR
446:    Higher order meshes are also supported
447: */
448: static PetscErrorCode DMPlexView_GLVis_ASCII(DM dm, PetscViewer viewer)
449: {
450:   DMLabel            label;
451:   PetscSection       coordSection, coordSectionCell, parentSection, hoSection = NULL;
452:   Vec                coordinates, coordinatesCell, hovec;
453:   const PetscScalar *array;
454:   PetscInt           bf, p, sdim, dim, depth, novl, minl;
455:   PetscInt           cStart, cEnd, vStart, vEnd, nvert;
456:   PetscMPIInt        size;
457:   PetscBool          localized, isascii;
458:   PetscBool          enable_mfem, enable_boundary, enable_ncmesh, view_ovl = PETSC_FALSE;
459:   PetscBT            pown, vown;
460:   PetscContainer     glvis_container;
461:   PetscBool          cellvertex = PETSC_FALSE, enabled = PETSC_TRUE;
462:   PetscBool          enable_emark, enable_bmark;
463:   const char        *fmt;
464:   char               emark[64] = "", bmark[64] = "";

466:   PetscFunctionBegin;
469:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
470:   PetscCheck(isascii, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Viewer must be of type VIEWERASCII");
471:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)viewer), &size));
472:   PetscCheck(size <= 1, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Use single sequential viewers for parallel visualization");
473:   PetscCall(DMGetDimension(dm, &dim));
474:   PetscCall(DMPlexGetDepth(dm, &depth));

476:   /* get container: determines if a process visualizes is portion of the data or not */
477:   PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&glvis_container));
478:   PetscCheck(glvis_container, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing GLVis container");
479:   {
480:     PetscViewerGLVisInfo glvis_info;
481:     PetscCall(PetscContainerGetPointer(glvis_container, &glvis_info));
482:     enabled = glvis_info->enabled;
483:     fmt     = glvis_info->fmt;
484:   }

486:   /* Users can attach a coordinate vector to the DM in case they have a higher-order mesh */
487:   PetscCall(PetscObjectQuery((PetscObject)dm, "_glvis_mesh_coords", (PetscObject *)&hovec));
488:   PetscCall(PetscObjectReference((PetscObject)hovec));
489:   if (!hovec) {
490:     DM           cdm;
491:     PetscFE      disc;
492:     PetscClassId classid;

494:     PetscCall(DMGetCoordinateDM(dm, &cdm));
495:     PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&disc));
496:     PetscCall(PetscObjectGetClassId((PetscObject)disc, &classid));
497:     if (classid == PETSCFE_CLASSID) {
498:       DM      hocdm;
499:       PetscFE hodisc;
500:       Vec     vec;
501:       Mat     mat;
502:       char    name[32], fec_type[64];
503:       IS      perm = NULL;

505:       PetscCall(GLVisCreateFE(disc, name, &hodisc, &perm));
506:       PetscCall(DMClone(cdm, &hocdm));
507:       PetscCall(DMSetField(hocdm, 0, NULL, (PetscObject)hodisc));
508:       PetscCall(PetscFEDestroy(&hodisc));
509:       PetscCall(DMCreateDS(hocdm));

511:       PetscCall(DMGetCoordinates(dm, &vec));
512:       PetscCall(DMCreateGlobalVector(hocdm, &hovec));
513:       PetscCall(DMCreateInterpolation(cdm, hocdm, &mat, NULL));
514:       PetscCall(MatInterpolate(mat, vec, hovec));
515:       PetscCall(MatDestroy(&mat));
516:       PetscCall(DMGetLocalSection(hocdm, &hoSection));
517:       PetscCall(PetscSectionSetClosurePermutation(hoSection, (PetscObject)hocdm, depth, perm));
518:       PetscCall(ISDestroy(&perm));
519:       PetscCall(DMDestroy(&hocdm));
520:       PetscCall(PetscSNPrintf(fec_type, sizeof(fec_type), "FiniteElementCollection: %s", name));
521:       PetscCall(PetscObjectSetName((PetscObject)hovec, fec_type));
522:     }
523:   }

525:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
526:   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &p, NULL));
527:   if (p >= 0) cEnd = p;
528:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
529:   PetscCall(DMGetCoordinatesLocalized(dm, &localized));
530:   PetscCall(DMGetCoordinateSection(dm, &coordSection));
531:   PetscCall(DMGetCoordinateDim(dm, &sdim));
532:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
533:   PetscCheck(coordinates || hovec, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Missing local coordinates vector");

535:   /*
536:      a couple of sections of the mesh specification are disabled
537:        - boundary: the boundary is not needed for proper mesh visualization unless we want to visualize boundary attributes or we have high-order coordinates in 3D (topologically)
538:        - vertex_parents: used for non-conforming meshes only when we want to use MFEM as a discretization package
539:                          and be able to derefine the mesh (MFEM does not currently have to ability to read ncmeshes in parallel)
540:   */
541:   enable_boundary = PETSC_FALSE;
542:   enable_ncmesh   = PETSC_FALSE;
543:   enable_mfem     = PETSC_FALSE;
544:   enable_emark    = PETSC_FALSE;
545:   enable_bmark    = PETSC_FALSE;
546:   /* I'm tired of problems with negative values in the markers, disable them */
547:   PetscOptionsBegin(PetscObjectComm((PetscObject)dm), ((PetscObject)dm)->prefix, "GLVis PetscViewer DMPlex Options", "PetscViewer");
548:   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_enable_boundary", "Enable boundary section in mesh representation", NULL, enable_boundary, &enable_boundary, NULL));
549:   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_enable_ncmesh", "Enable vertex_parents section in mesh representation (allows derefinement)", NULL, enable_ncmesh, &enable_ncmesh, NULL));
550:   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_enable_mfem", "Dump a mesh that can be used with MFEM's FiniteElementSpaces", NULL, enable_mfem, &enable_mfem, NULL));
551:   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_overlap", "Include overlap region in local meshes", NULL, view_ovl, &view_ovl, NULL));
552:   PetscCall(PetscOptionsString("-viewer_glvis_dm_plex_emarker", "String for the material id label", NULL, emark, emark, sizeof(emark), &enable_emark));
553:   PetscCall(PetscOptionsString("-viewer_glvis_dm_plex_bmarker", "String for the boundary id label", NULL, bmark, bmark, sizeof(bmark), &enable_bmark));
554:   PetscOptionsEnd();
555:   if (enable_bmark) enable_boundary = PETSC_TRUE;

557:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
558:   PetscCheck(!enable_ncmesh || size == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported in parallel");
559:   PetscCheck(!enable_boundary || depth < 0 || dim == depth, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG,
560:              "Mesh must be interpolated. "
561:              "Alternatively, run with -viewer_glvis_dm_plex_enable_boundary 0");
562:   PetscCheck(!enable_ncmesh || depth < 0 || dim == depth, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG,
563:              "Mesh must be interpolated. "
564:              "Alternatively, run with -viewer_glvis_dm_plex_enable_ncmesh 0");
565:   if (depth >= 0 && dim != depth) { /* not interpolated, it assumes cell-vertex mesh */
566:     PetscCheck(depth == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported depth %" PetscInt_FMT ". You should interpolate the mesh first", depth);
567:     cellvertex = PETSC_TRUE;
568:   }

570:   /* Identify possible cells in the overlap */
571:   novl = 0;
572:   pown = NULL;
573:   if (size > 1) {
574:     IS              globalNum = NULL;
575:     const PetscInt *gNum;
576:     PetscBool       ovl = PETSC_FALSE;

578:     PetscCall(PetscObjectQuery((PetscObject)dm, "_glvis_plex_gnum", (PetscObject *)&globalNum));
579:     if (!globalNum) {
580:       if (view_ovl) {
581:         PetscCall(ISCreateStride(PetscObjectComm((PetscObject)dm), cEnd - cStart, 0, 1, &globalNum));
582:       } else {
583:         PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &globalNum));
584:       }
585:       PetscCall(PetscObjectCompose((PetscObject)dm, "_glvis_plex_gnum", (PetscObject)globalNum));
586:       PetscCall(PetscObjectDereference((PetscObject)globalNum));
587:     }
588:     PetscCall(ISGetIndices(globalNum, &gNum));
589:     for (p = cStart; p < cEnd; p++) {
590:       if (gNum[p - cStart] < 0) {
591:         ovl = PETSC_TRUE;
592:         novl++;
593:       }
594:     }
595:     if (ovl) {
596:       /* it may happen that pown get not destroyed, if the user closes the window while this function is running.
597:          TODO: garbage collector? attach pown to dm?  */
598:       PetscCall(PetscBTCreate(cEnd - cStart, &pown));
599:       for (p = cStart; p < cEnd; p++) {
600:         if (gNum[p - cStart] < 0) continue;
601:         else PetscCall(PetscBTSet(pown, p - cStart));
602:       }
603:     }
604:     PetscCall(ISRestoreIndices(globalNum, &gNum));
605:   }

607:   /* vertex_parents (Non-conforming meshes) */
608:   parentSection = NULL;
609:   if (enable_ncmesh) {
610:     PetscCall(DMPlexGetTree(dm, &parentSection, NULL, NULL, NULL, NULL));
611:     enable_ncmesh = (PetscBool)(enable_ncmesh && parentSection);
612:   }
613:   /* return if this process is disabled */
614:   if (!enabled) {
615:     PetscCall(PetscViewerASCIIPrintf(viewer, "MFEM mesh %s\n", enable_ncmesh ? "v1.1" : "v1.0"));
616:     PetscCall(PetscViewerASCIIPrintf(viewer, "\ndimension\n"));
617:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", dim));
618:     PetscCall(PetscViewerASCIIPrintf(viewer, "\nelements\n"));
619:     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
620:     PetscCall(PetscViewerASCIIPrintf(viewer, "\nboundary\n"));
621:     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
622:     PetscCall(PetscViewerASCIIPrintf(viewer, "\nvertices\n"));
623:     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
624:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", sdim));
625:     PetscCall(PetscBTDestroy(&pown));
626:     PetscCall(VecDestroy(&hovec));
627:     PetscFunctionReturn(PETSC_SUCCESS);
628:   }

630:   if (enable_mfem) {
631:     if (localized && !hovec) { /* we need to generate a vector of L2 coordinates, as this is how MFEM handles periodic meshes */
632:       PetscInt     vpc = 0;
633:       char         fec[64];
634:       PetscInt     vids[8]  = {0, 1, 2, 3, 4, 5, 6, 7};
635:       PetscInt     hexv[8]  = {0, 1, 3, 2, 4, 5, 7, 6};
636:       PetscInt     quadv[8] = {0, 1, 3, 2}, triv[3] = {0, 1, 2};
637:       PetscInt    *dof = NULL;
638:       PetscScalar *array, *ptr;

640:       PetscCall(PetscSNPrintf(fec, sizeof(fec), "FiniteElementCollection: L2_T1_%" PetscInt_FMT "D_P1", dim));
641:       if (cEnd - cStart) {
642:         PetscInt fpc;

644:         PetscCall(DMPlexGetConeSize(dm, cStart, &fpc));
645:         switch (dim) {
646:         case 1:
647:           vpc = 2;
648:           dof = hexv;
649:           break;
650:         case 2:
651:           switch (fpc) {
652:           case 3:
653:             vpc = 3;
654:             dof = triv;
655:             break;
656:           case 4:
657:             vpc = 4;
658:             dof = quadv;
659:             break;
660:           default:
661:             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unhandled case: faces per cell %" PetscInt_FMT, fpc);
662:           }
663:           break;
664:         case 3:
665:           switch (fpc) {
666:           case 4: /* TODO: still need to understand L2 ordering for tets */
667:             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unhandled tethraedral case");
668:           case 6:
669:             PetscCheck(!cellvertex, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unhandled case: vertices per cell %" PetscInt_FMT, fpc);
670:             vpc = 8;
671:             dof = hexv;
672:             break;
673:           case 8:
674:             PetscCheck(cellvertex, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unhandled case: faces per cell %" PetscInt_FMT, fpc);
675:             vpc = 8;
676:             dof = hexv;
677:             break;
678:           default:
679:             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unhandled case: faces per cell %" PetscInt_FMT, fpc);
680:           }
681:           break;
682:         default:
683:           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unhandled dim");
684:         }
685:         PetscCall(DMPlexReorderCell(dm, cStart, vids));
686:       }
687:       PetscCheck(dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing dofs");
688:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, (cEnd - cStart - novl) * vpc * sdim, &hovec));
689:       PetscCall(PetscObjectSetName((PetscObject)hovec, fec));
690:       PetscCall(VecGetArray(hovec, &array));
691:       ptr = array;
692:       for (p = cStart; p < cEnd; p++) {
693:         PetscInt     csize, v, d;
694:         PetscScalar *vals = NULL;

696:         if (PetscUnlikely(pown && !PetscBTLookup(pown, p - cStart))) continue;
697:         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, p, &csize, &vals));
698:         PetscCheck(csize == vpc * sdim || csize == vpc * sdim * 2, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported closure size %" PetscInt_FMT " (vpc %" PetscInt_FMT ", sdim %" PetscInt_FMT ")", csize, vpc, sdim);
699:         for (v = 0; v < vpc; v++) {
700:           for (d = 0; d < sdim; d++) ptr[sdim * dof[v] + d] = vals[sdim * vids[v] + d];
701:         }
702:         ptr += vpc * sdim;
703:         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, p, &csize, &vals));
704:       }
705:       PetscCall(VecRestoreArray(hovec, &array));
706:     }
707:   }
708:   /* if we have high-order coordinates in 3D, we need to specify the boundary */
709:   if (hovec && dim == 3) enable_boundary = PETSC_TRUE;

711:   /* header */
712:   PetscCall(PetscViewerASCIIPrintf(viewer, "MFEM mesh %s\n", enable_ncmesh ? "v1.1" : "v1.0"));

714:   /* topological dimension */
715:   PetscCall(PetscViewerASCIIPrintf(viewer, "\ndimension\n"));
716:   PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", dim));

718:   /* elements */
719:   minl  = 1;
720:   label = NULL;
721:   if (enable_emark) {
722:     minl = PETSC_INT_MAX;
723:     PetscCall(DMGetLabel(dm, emark, &label));
724:     if (label) {
725:       IS       vals;
726:       PetscInt ldef;

728:       PetscCall(DMLabelGetDefaultValue(label, &ldef));
729:       PetscCall(DMLabelGetValueIS(label, &vals));
730:       PetscCall(ISGetMinMax(vals, &minl, NULL));
731:       PetscCall(ISDestroy(&vals));
732:       minl = PetscMin(ldef, minl);
733:     }
734:     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &minl, 1, MPIU_INT, MPI_MIN, PetscObjectComm((PetscObject)dm)));
735:     if (minl == PETSC_INT_MAX) minl = 1;
736:   }
737:   PetscCall(PetscViewerASCIIPrintf(viewer, "\nelements\n"));
738:   PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", cEnd - cStart - novl));
739:   for (p = cStart; p < cEnd; p++) {
740:     PetscInt vids[8];
741:     PetscInt i, nv = 0, cid = -1, mid = 1;

743:     if (PetscUnlikely(pown && !PetscBTLookup(pown, p - cStart))) continue;
744:     PetscCall(DMPlexGetPointMFEMCellID_Internal(dm, label, minl, p, &mid, &cid));
745:     PetscCall(DMPlexGetPointMFEMVertexIDs_Internal(dm, p, (localized && !hovec) ? coordSection : NULL, &nv, vids));
746:     PetscCall(DMPlexReorderCell(dm, p, vids));
747:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " %" PetscInt_FMT, mid, cid));
748:     for (i = 0; i < nv; i++) PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, vids[i]));
749:     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
750:   }

752:   /* boundary */
753:   PetscCall(PetscViewerASCIIPrintf(viewer, "\nboundary\n"));
754:   if (!enable_boundary) {
755:     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
756:   } else {
757:     DMLabel  perLabel;
758:     PetscBT  bfaces;
759:     PetscInt fStart, fEnd, *fcells;

761:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
762:     PetscCall(PetscBTCreate(fEnd - fStart, &bfaces));
763:     PetscCall(DMPlexGetMaxSizes(dm, NULL, &p));
764:     PetscCall(PetscMalloc1(p, &fcells));
765:     PetscCall(DMGetLabel(dm, "glvis_periodic_cut", &perLabel));
766:     if (!perLabel && localized) { /* this periodic cut can be moved up to DMPlex setup */
767:       PetscCall(DMCreateLabel(dm, "glvis_periodic_cut"));
768:       PetscCall(DMGetLabel(dm, "glvis_periodic_cut", &perLabel));
769:       PetscCall(DMLabelSetDefaultValue(perLabel, 1));
770:       PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
771:       PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
772:       for (p = cStart; p < cEnd; p++) {
773:         DMPolytopeType cellType;
774:         PetscInt       dof;

776:         PetscCall(DMPlexGetCellType(dm, p, &cellType));
777:         PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
778:         if (dof) {
779:           PetscInt     uvpc, v, csize, csizeCell, cellClosureSize, *cellClosure = NULL, *vidxs = NULL;
780:           PetscScalar *vals = NULL, *valsCell = NULL;

782:           uvpc = DMPolytopeTypeGetNumVertices(cellType);
783:           PetscCheck(dof % sdim == 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Incompatible number of cell dofs %" PetscInt_FMT " and space dimension %" PetscInt_FMT, dof, sdim);
784:           PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, p, &csize, &vals));
785:           PetscCall(DMPlexVecGetClosure(dm, coordSectionCell, coordinatesCell, p, &csizeCell, &valsCell));
786:           PetscCheck(csize == csizeCell, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Cell %" PetscInt_FMT " has invalid localized coordinates", p);
787:           PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cellClosureSize, &cellClosure));
788:           for (v = 0; v < cellClosureSize; v++)
789:             if (cellClosure[2 * v] >= vStart && cellClosure[2 * v] < vEnd) {
790:               vidxs = cellClosure + 2 * v;
791:               break;
792:             }
793:           PetscCheck(vidxs, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Missing vertices");
794:           for (v = 0; v < uvpc; v++) {
795:             for (PetscInt s = 0; s < sdim; s++) {
796:               if (PetscAbsScalar(vals[v * sdim + s] - valsCell[v * sdim + s]) > PETSC_MACHINE_EPSILON) PetscCall(DMLabelSetValue(perLabel, vidxs[2 * v], 2));
797:             }
798:           }
799:           PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cellClosureSize, &cellClosure));
800:           PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, p, &csize, &vals));
801:           PetscCall(DMPlexVecRestoreClosure(dm, coordSectionCell, coordinatesCell, p, &csizeCell, &valsCell));
802:         }
803:       }
804:       if (dim > 1) {
805:         PetscInt eEnd, eStart;

807:         PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
808:         for (p = eStart; p < eEnd; p++) {
809:           const PetscInt *cone;
810:           PetscInt        coneSize, i;
811:           PetscBool       ispe = PETSC_TRUE;

813:           PetscCall(DMPlexGetCone(dm, p, &cone));
814:           PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
815:           for (i = 0; i < coneSize; i++) {
816:             PetscInt v;

818:             PetscCall(DMLabelGetValue(perLabel, cone[i], &v));
819:             ispe = (PetscBool)(ispe && (v == 2));
820:           }
821:           if (ispe && coneSize) {
822:             PetscInt        numChildren;
823:             const PetscInt *children;

825:             PetscCall(DMLabelSetValue(perLabel, p, 2));
826:             PetscCall(DMPlexGetTreeChildren(dm, p, &numChildren, &children));
827:             for (PetscInt ch = 0; ch < numChildren; ch++) PetscCall(DMLabelSetValue(perLabel, children[ch], 2));
828:           }
829:         }
830:         if (dim > 2) {
831:           for (p = fStart; p < fEnd; p++) {
832:             const PetscInt *cone;
833:             PetscInt        coneSize;
834:             PetscBool       ispe = PETSC_TRUE;

836:             PetscCall(DMPlexGetCone(dm, p, &cone));
837:             PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
838:             for (PetscInt i = 0; i < coneSize; i++) {
839:               PetscInt v;

841:               PetscCall(DMLabelGetValue(perLabel, cone[i], &v));
842:               ispe = (PetscBool)(ispe && (v == 2));
843:             }
844:             if (ispe && coneSize) {
845:               PetscInt        numChildren;
846:               const PetscInt *children;

848:               PetscCall(DMLabelSetValue(perLabel, p, 2));
849:               PetscCall(DMPlexGetTreeChildren(dm, p, &numChildren, &children));
850:               for (PetscInt ch = 0; ch < numChildren; ch++) PetscCall(DMLabelSetValue(perLabel, children[ch], 2));
851:             }
852:           }
853:         }
854:       }
855:     }
856:     for (p = fStart; p < fEnd; p++) {
857:       const PetscInt *support;
858:       PetscInt        supportSize;
859:       PetscBool       isbf = PETSC_FALSE;

861:       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
862:       if (pown) {
863:         PetscBool has_owned = PETSC_FALSE, has_ghost = PETSC_FALSE;
864:         PetscInt  i;

866:         PetscCall(DMPlexGetSupport(dm, p, &support));
867:         for (i = 0; i < supportSize; i++) {
868:           if (PetscLikely(PetscBTLookup(pown, support[i] - cStart))) has_owned = PETSC_TRUE;
869:           else has_ghost = PETSC_TRUE;
870:         }
871:         isbf = (PetscBool)((supportSize == 1 && has_owned) || (supportSize > 1 && has_owned && has_ghost));
872:       } else {
873:         isbf = (PetscBool)(supportSize == 1);
874:       }
875:       if (!isbf && perLabel) {
876:         const PetscInt *cone;
877:         PetscInt        coneSize;

879:         PetscCall(DMPlexGetCone(dm, p, &cone));
880:         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
881:         isbf = PETSC_TRUE;
882:         for (PetscInt i = 0; i < coneSize; i++) {
883:           PetscInt v, d;

885:           PetscCall(DMLabelGetValue(perLabel, cone[i], &v));
886:           PetscCall(DMLabelGetDefaultValue(perLabel, &d));
887:           isbf = (PetscBool)(isbf && v != d);
888:         }
889:       }
890:       if (isbf) PetscCall(PetscBTSet(bfaces, p - fStart));
891:     }
892:     /* count boundary faces */
893:     for (p = fStart, bf = 0; p < fEnd; p++) {
894:       if (PetscUnlikely(PetscBTLookup(bfaces, p - fStart))) {
895:         const PetscInt *support;
896:         PetscInt        supportSize;

898:         PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
899:         PetscCall(DMPlexGetSupport(dm, p, &support));
900:         for (PetscInt c = 0; c < supportSize; c++) {
901:           const PetscInt *cone;
902:           PetscInt        cell, cl, coneSize;

904:           cell = support[c];
905:           if (pown && PetscUnlikely(!PetscBTLookup(pown, cell - cStart))) continue;
906:           PetscCall(DMPlexGetCone(dm, cell, &cone));
907:           PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
908:           for (cl = 0; cl < coneSize; cl++) {
909:             if (cone[cl] == p) {
910:               bf += 1;
911:               break;
912:             }
913:           }
914:         }
915:       }
916:     }
917:     minl  = 1;
918:     label = NULL;
919:     if (enable_bmark) {
920:       minl = PETSC_INT_MAX;
921:       PetscCall(DMGetLabel(dm, bmark, &label));
922:       if (label) {
923:         IS       vals;
924:         PetscInt ldef;

926:         PetscCall(DMLabelGetDefaultValue(label, &ldef));
927:         PetscCall(DMLabelGetValueIS(label, &vals));
928:         PetscCall(ISGetMinMax(vals, &minl, NULL));
929:         PetscCall(ISDestroy(&vals));
930:         minl = PetscMin(ldef, minl);
931:       }
932:       PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &minl, 1, MPIU_INT, MPI_MIN, PetscObjectComm((PetscObject)dm)));
933:       if (minl == PETSC_INT_MAX) minl = 1;
934:     }
935:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", bf));
936:     for (p = fStart; p < fEnd; p++) {
937:       if (PetscUnlikely(PetscBTLookup(bfaces, p - fStart))) {
938:         const PetscInt *support;
939:         PetscInt        supportSize, c, nc = 0;

941:         PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
942:         PetscCall(DMPlexGetSupport(dm, p, &support));
943:         if (pown) {
944:           for (c = 0; c < supportSize; c++) {
945:             if (PetscLikely(PetscBTLookup(pown, support[c] - cStart))) fcells[nc++] = support[c];
946:           }
947:         } else
948:           for (c = 0; c < supportSize; c++) fcells[nc++] = support[c];
949:         for (c = 0; c < nc; c++) {
950:           const DMPolytopeType *faceTypes;
951:           DMPolytopeType        cellType;
952:           const PetscInt       *faceSizes, *cone;
953:           PetscInt              vids[8], *faces, st, i, coneSize, cell, cl, nv, cid = -1, mid = -1;

955:           cell = fcells[c];
956:           PetscCall(DMPlexGetCone(dm, cell, &cone));
957:           PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
958:           for (cl = 0; cl < coneSize; cl++)
959:             if (cone[cl] == p) break;
960:           if (cl == coneSize) continue;

962:           /* face material id and type */
963:           PetscCall(DMPlexGetPointMFEMCellID_Internal(dm, label, minl, p, &mid, &cid));
964:           PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " %" PetscInt_FMT, mid, cid));
965:           /* vertex ids */
966:           PetscCall(DMPlexGetCellType(dm, cell, &cellType));
967:           PetscCall(DMPlexGetPointMFEMVertexIDs_Internal(dm, cell, (localized && !hovec) ? coordSection : NULL, &nv, vids));
968:           PetscCall(DMPlexGetRawFaces_Internal(dm, cellType, vids, NULL, &faceTypes, &faceSizes, (const PetscInt **)&faces));
969:           st = 0;
970:           for (i = 0; i < cl; i++) st += faceSizes[i];
971:           PetscCall(DMPlexInvertCell(faceTypes[cl], faces + st));
972:           for (i = 0; i < faceSizes[cl]; i++) PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, faces[st + i]));
973:           PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
974:           PetscCall(DMPlexRestoreRawFaces_Internal(dm, cellType, vids, NULL, &faceTypes, &faceSizes, (const PetscInt **)&faces));
975:           bf -= 1;
976:         }
977:       }
978:     }
979:     PetscCheck(!bf, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Remaining boundary faces %" PetscInt_FMT, bf);
980:     PetscCall(PetscBTDestroy(&bfaces));
981:     PetscCall(PetscFree(fcells));
982:   }

984:   /* mark owned vertices */
985:   vown = NULL;
986:   if (pown) {
987:     PetscCall(PetscBTCreate(vEnd - vStart, &vown));
988:     for (p = cStart; p < cEnd; p++) {
989:       PetscInt i, closureSize, *closure = NULL;

991:       if (PetscUnlikely(!PetscBTLookup(pown, p - cStart))) continue;
992:       PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
993:       for (i = 0; i < closureSize; i++) {
994:         const PetscInt pp = closure[2 * i];

996:         if (pp >= vStart && pp < vEnd) PetscCall(PetscBTSet(vown, pp - vStart));
997:       }
998:       PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
999:     }
1000:   }

1002:   if (parentSection) {
1003:     PetscInt vp, gvp;

1005:     for (vp = 0, p = vStart; p < vEnd; p++) {
1006:       DMLabel  dlabel;
1007:       PetscInt parent, depth;

1009:       if (PetscUnlikely(vown && !PetscBTLookup(vown, p - vStart))) continue;
1010:       PetscCall(DMPlexGetDepthLabel(dm, &dlabel));
1011:       PetscCall(DMLabelGetValue(dlabel, p, &depth));
1012:       PetscCall(DMPlexGetTreeParent(dm, p, &parent, NULL));
1013:       if (parent != p) vp++;
1014:     }
1015:     PetscCallMPI(MPIU_Allreduce(&vp, &gvp, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
1016:     if (gvp) {
1017:       PetscInt   maxsupp;
1018:       PetscBool *skip = NULL;

1020:       PetscCall(PetscViewerASCIIPrintf(viewer, "\nvertex_parents\n"));
1021:       PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", vp));
1022:       PetscCall(DMPlexGetMaxSizes(dm, NULL, &maxsupp));
1023:       PetscCall(PetscMalloc1(maxsupp, &skip));
1024:       for (p = vStart; p < vEnd; p++) {
1025:         DMLabel  dlabel;
1026:         PetscInt parent;

1028:         if (PetscUnlikely(vown && !PetscBTLookup(vown, p - vStart))) continue;
1029:         PetscCall(DMPlexGetDepthLabel(dm, &dlabel));
1030:         PetscCall(DMPlexGetTreeParent(dm, p, &parent, NULL));
1031:         if (parent != p) {
1032:           PetscInt        vids[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* silent overzealous clang static analyzer */
1033:           PetscInt        i, nv, ssize, n, numChildren, depth = -1;
1034:           const PetscInt *children;

1036:           PetscCall(DMPlexGetConeSize(dm, parent, &ssize));
1037:           switch (ssize) {
1038:           case 2: /* edge */
1039:             nv = 0;
1040:             PetscCall(DMPlexGetPointMFEMVertexIDs_Internal(dm, parent, localized ? coordSection : NULL, &nv, vids));
1041:             PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT, p - vStart));
1042:             for (i = 0; i < nv; i++) PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, vids[i]));
1043:             PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1044:             vp--;
1045:             break;
1046:           case 4: /* face */
1047:             PetscCall(DMPlexGetTreeChildren(dm, parent, &numChildren, &children));
1048:             for (n = 0; n < numChildren; n++) {
1049:               PetscCall(DMLabelGetValue(dlabel, children[n], &depth));
1050:               if (!depth) {
1051:                 const PetscInt *hvsupp, *hesupp, *cone;
1052:                 PetscInt        hvsuppSize, hesuppSize, coneSize;
1053:                 PetscInt        hv = children[n], he = -1, f;

1055:                 PetscCall(PetscArrayzero(skip, maxsupp));
1056:                 PetscCall(DMPlexGetSupportSize(dm, hv, &hvsuppSize));
1057:                 PetscCall(DMPlexGetSupport(dm, hv, &hvsupp));
1058:                 for (i = 0; i < hvsuppSize; i++) {
1059:                   PetscInt ep;
1060:                   PetscCall(DMPlexGetTreeParent(dm, hvsupp[i], &ep, NULL));
1061:                   if (ep != hvsupp[i]) {
1062:                     he = hvsupp[i];
1063:                   } else {
1064:                     skip[i] = PETSC_TRUE;
1065:                   }
1066:                 }
1067:                 PetscCheck(he != -1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Vertex %" PetscInt_FMT " support size %" PetscInt_FMT ": hanging edge not found", hv, hvsuppSize);
1068:                 PetscCall(DMPlexGetCone(dm, he, &cone));
1069:                 vids[0] = (cone[0] == hv) ? cone[1] : cone[0];
1070:                 PetscCall(DMPlexGetSupportSize(dm, he, &hesuppSize));
1071:                 PetscCall(DMPlexGetSupport(dm, he, &hesupp));
1072:                 for (f = 0; f < hesuppSize; f++) {
1073:                   PetscCall(DMPlexGetCone(dm, hesupp[f], &cone));
1074:                   PetscCall(DMPlexGetConeSize(dm, hesupp[f], &coneSize));
1075:                   for (PetscInt j = 0; j < coneSize; j++) {
1076:                     for (PetscInt k = 0; k < hvsuppSize; k++) {
1077:                       if (hvsupp[k] == cone[j]) {
1078:                         skip[k] = PETSC_TRUE;
1079:                         break;
1080:                       }
1081:                     }
1082:                   }
1083:                 }
1084:                 for (i = 0; i < hvsuppSize; i++) {
1085:                   if (!skip[i]) {
1086:                     PetscCall(DMPlexGetCone(dm, hvsupp[i], &cone));
1087:                     vids[1] = (cone[0] == hv) ? cone[1] : cone[0];
1088:                   }
1089:                 }
1090:                 PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT, hv - vStart));
1091:                 for (i = 0; i < 2; i++) PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, vids[i] - vStart));
1092:                 PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1093:                 vp--;
1094:               }
1095:             }
1096:             break;
1097:           default:
1098:             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Don't know how to deal with support size %" PetscInt_FMT, ssize);
1099:           }
1100:         }
1101:       }
1102:       PetscCall(PetscFree(skip));
1103:     }
1104:     PetscCheck(!vp, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " hanging vertices", vp);
1105:   }
1106:   PetscCall(PetscBTDestroy(&vown));

1108:   /* vertices */
1109:   if (hovec) { /* higher-order meshes */
1110:     const char *fec;
1111:     PetscInt    i, n, s;
1112:     PetscCall(PetscViewerASCIIPrintf(viewer, "\nvertices\n"));
1113:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", vEnd - vStart));
1114:     PetscCall(PetscViewerASCIIPrintf(viewer, "nodes\n"));
1115:     PetscCall(PetscObjectGetName((PetscObject)hovec, &fec));
1116:     PetscCall(PetscViewerASCIIPrintf(viewer, "FiniteElementSpace\n"));
1117:     PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", fec));
1118:     PetscCall(PetscViewerASCIIPrintf(viewer, "VDim: %" PetscInt_FMT "\n", sdim));
1119:     PetscCall(PetscViewerASCIIPrintf(viewer, "Ordering: 1\n\n")); /*Ordering::byVDIM*/
1120:     if (hoSection) {
1121:       DM cdm;

1123:       PetscCall(VecGetDM(hovec, &cdm));
1124:       for (p = cStart; p < cEnd; p++) {
1125:         PetscScalar *vals = NULL;
1126:         PetscInt     csize;

1128:         if (PetscUnlikely(pown && !PetscBTLookup(pown, p - cStart))) continue;
1129:         PetscCall(DMPlexVecGetClosure(cdm, hoSection, hovec, p, &csize, &vals));
1130:         PetscCheck(csize % sdim == 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Size of closure %" PetscInt_FMT " incompatible with space dimension %" PetscInt_FMT, csize, sdim);
1131:         for (i = 0; i < csize / sdim; i++) {
1132:           for (s = 0; s < sdim; s++) PetscCall(PetscViewerASCIIPrintf(viewer, fmt, (double)PetscRealPart(vals[i * sdim + s])));
1133:           PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1134:         }
1135:         PetscCall(DMPlexVecRestoreClosure(cdm, hoSection, hovec, p, &csize, &vals));
1136:       }
1137:     } else {
1138:       PetscCall(VecGetArrayRead(hovec, &array));
1139:       PetscCall(VecGetLocalSize(hovec, &n));
1140:       PetscCheck(n % sdim == 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Size of local coordinate vector %" PetscInt_FMT " incompatible with space dimension %" PetscInt_FMT, n, sdim);
1141:       for (i = 0; i < n / sdim; i++) {
1142:         for (s = 0; s < sdim; s++) PetscCall(PetscViewerASCIIPrintf(viewer, fmt, (double)PetscRealPart(array[i * sdim + s])));
1143:         PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1144:       }
1145:       PetscCall(VecRestoreArrayRead(hovec, &array));
1146:     }
1147:   } else {
1148:     PetscCall(VecGetLocalSize(coordinates, &nvert));
1149:     PetscCall(PetscViewerASCIIPrintf(viewer, "\nvertices\n"));
1150:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", nvert / sdim));
1151:     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", sdim));
1152:     PetscCall(VecGetArrayRead(coordinates, &array));
1153:     for (p = 0; p < nvert / sdim; p++) {
1154:       for (PetscInt s = 0; s < sdim; s++) {
1155:         PetscReal v = PetscRealPart(array[p * sdim + s]);

1157:         PetscCall(PetscViewerASCIIPrintf(viewer, fmt, PetscIsInfOrNanReal(v) ? 0.0 : (double)v));
1158:       }
1159:       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1160:     }
1161:     PetscCall(VecRestoreArrayRead(coordinates, &array));
1162:   }
1163:   PetscCall(PetscBTDestroy(&pown));
1164:   PetscCall(VecDestroy(&hovec));
1165:   PetscFunctionReturn(PETSC_SUCCESS);
1166: }

1168: PetscErrorCode DMPlexView_GLVis(DM dm, PetscViewer viewer)
1169: {
1170:   PetscFunctionBegin;
1171:   PetscCall(DMView_GLVis(dm, viewer, DMPlexView_GLVis_ASCII));
1172:   PetscFunctionReturn(PETSC_SUCCESS);
1173: }