xref: /petsc/src/dm/impls/plex/plexglvis.c (revision e84e3fd21fa5912dca3017339ab4b3699e3a9c51)
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>
8 
9 typedef struct {
10   PetscInt   nf;
11   VecScatter *scctx;
12 } GLVisViewerCtx;
13 
14 static PetscErrorCode DestroyGLVisViewerCtx_Private(void *vctx)
15 {
16   GLVisViewerCtx *ctx = (GLVisViewerCtx*)vctx;
17   PetscInt       i;
18 
19   PetscFunctionBegin;
20   for (i=0;i<ctx->nf;i++) {
21     PetscCall(VecScatterDestroy(&ctx->scctx[i]));
22   }
23   PetscCall(PetscFree(ctx->scctx));
24   PetscCall(PetscFree(vctx));
25   PetscFunctionReturn(0);
26 }
27 
28 static PetscErrorCode DMPlexSampleGLVisFields_Private(PetscObject oX, PetscInt nf, PetscObject oXfield[], void *vctx)
29 {
30   GLVisViewerCtx *ctx = (GLVisViewerCtx*)vctx;
31   PetscInt       f;
32 
33   PetscFunctionBegin;
34   for (f=0;f<nf;f++) {
35     PetscCall(VecScatterBegin(ctx->scctx[f],(Vec)oX,(Vec)oXfield[f],INSERT_VALUES,SCATTER_FORWARD));
36     PetscCall(VecScatterEnd(ctx->scctx[f],(Vec)oX,(Vec)oXfield[f],INSERT_VALUES,SCATTER_FORWARD));
37   }
38   PetscFunctionReturn(0);
39 }
40 
41 /* for FEM, it works for H1 fields only and extracts dofs at cell vertices, discarding any other dof */
42 PetscErrorCode DMSetUpGLVisViewer_Plex(PetscObject odm, PetscViewer viewer)
43 {
44   DM             dm = (DM)odm;
45   Vec            xlocal,xfield,*Ufield;
46   PetscDS        ds;
47   IS             globalNum,isfield;
48   PetscBT        vown;
49   char           **fieldname = NULL,**fec_type = NULL;
50   const PetscInt *gNum;
51   PetscInt       *nlocal,*bs,*idxs,*dims;
52   PetscInt       f,maxfields,nfields,c,totc,totdofs,Nv,cum,i;
53   PetscInt       dim,cStart,cEnd,vStart,vEnd;
54   GLVisViewerCtx *ctx;
55   PetscSection   s;
56 
57   PetscFunctionBegin;
58   PetscCall(DMGetDimension(dm,&dim));
59   PetscCall(DMPlexGetDepthStratum(dm,0,&vStart,&vEnd));
60   PetscCall(DMPlexGetHeightStratum(dm,0,&cStart,&cEnd));
61   PetscCall(PetscObjectQuery((PetscObject)dm,"_glvis_plex_gnum",(PetscObject*)&globalNum));
62   if (!globalNum) {
63     PetscCall(DMPlexCreateCellNumbering_Internal(dm,PETSC_TRUE,&globalNum));
64     PetscCall(PetscObjectCompose((PetscObject)dm,"_glvis_plex_gnum",(PetscObject)globalNum));
65     PetscCall(PetscObjectDereference((PetscObject)globalNum));
66   }
67   PetscCall(ISGetIndices(globalNum,&gNum));
68   PetscCall(PetscBTCreate(vEnd-vStart,&vown));
69   for (c = cStart, totc = 0; c < cEnd; c++) {
70     if (gNum[c-cStart] >= 0) {
71       PetscInt i,numPoints,*points = NULL;
72 
73       totc++;
74       PetscCall(DMPlexGetTransitiveClosure(dm,c,PETSC_TRUE,&numPoints,&points));
75       for (i=0;i<numPoints*2;i+= 2) {
76         if ((points[i] >= vStart) && (points[i] < vEnd)) {
77           PetscCall(PetscBTSet(vown,points[i]-vStart));
78         }
79       }
80       PetscCall(DMPlexRestoreTransitiveClosure(dm,c,PETSC_TRUE,&numPoints,&points));
81     }
82   }
83   for (f=0,Nv=0;f<vEnd-vStart;f++) if (PetscLikely(PetscBTLookup(vown,f))) Nv++;
84 
85   PetscCall(DMCreateLocalVector(dm,&xlocal));
86   PetscCall(VecGetLocalSize(xlocal,&totdofs));
87   PetscCall(DMGetLocalSection(dm,&s));
88   PetscCall(PetscSectionGetNumFields(s,&nfields));
89   for (f=0,maxfields=0;f<nfields;f++) {
90     PetscInt bs;
91 
92     PetscCall(PetscSectionGetFieldComponents(s,f,&bs));
93     maxfields += bs;
94   }
95   PetscCall(PetscCalloc7(maxfields,&fieldname,maxfields,&nlocal,maxfields,&bs,maxfields,&dims,maxfields,&fec_type,totdofs,&idxs,maxfields,&Ufield));
96   PetscCall(PetscNew(&ctx));
97   PetscCall(PetscCalloc1(maxfields,&ctx->scctx));
98   PetscCall(DMGetDS(dm,&ds));
99   if (ds) {
100     for (f=0;f<nfields;f++) {
101       const char* fname;
102       char        name[256];
103       PetscObject disc;
104       size_t      len;
105 
106       PetscCall(PetscSectionGetFieldName(s,f,&fname));
107       PetscCall(PetscStrlen(fname,&len));
108       if (len) {
109         PetscCall(PetscStrcpy(name,fname));
110       } else {
111         PetscCall(PetscSNPrintf(name,256,"Field%D",f));
112       }
113       PetscCall(PetscDSGetDiscretization(ds,f,&disc));
114       if (disc) {
115         PetscClassId id;
116         PetscInt     Nc;
117         char         fec[64];
118 
119         PetscCall(PetscObjectGetClassId(disc, &id));
120         if (id == PETSCFE_CLASSID) {
121           PetscFE            fem = (PetscFE)disc;
122           PetscDualSpace     sp;
123           PetscDualSpaceType spname;
124           PetscInt           order;
125           PetscBool          islag,continuous,H1 = PETSC_TRUE;
126 
127           PetscCall(PetscFEGetNumComponents(fem,&Nc));
128           PetscCall(PetscFEGetDualSpace(fem,&sp));
129           PetscCall(PetscDualSpaceGetType(sp,&spname));
130           PetscCall(PetscStrcmp(spname,PETSCDUALSPACELAGRANGE,&islag));
131           PetscCheck(islag,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Unsupported dual space");
132           PetscCall(PetscDualSpaceLagrangeGetContinuity(sp,&continuous));
133           PetscCall(PetscDualSpaceGetOrder(sp,&order));
134           if (continuous && order > 0) { /* no support for high-order viz, still have to figure out the numbering */
135             PetscCall(PetscSNPrintf(fec,64,"FiniteElementCollection: H1_%DD_P1",dim));
136           } else {
137             PetscCheck(continuous || !order,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Discontinuous space visualization currently unsupported for order %D",order);
138             H1   = PETSC_FALSE;
139             PetscCall(PetscSNPrintf(fec,64,"FiniteElementCollection: L2_%DD_P%D",dim,order));
140           }
141           PetscCall(PetscStrallocpy(name,&fieldname[ctx->nf]));
142           bs[ctx->nf]   = Nc;
143           dims[ctx->nf] = dim;
144           if (H1) {
145             nlocal[ctx->nf] = Nc * Nv;
146             PetscCall(PetscStrallocpy(fec,&fec_type[ctx->nf]));
147             PetscCall(VecCreateSeq(PETSC_COMM_SELF,Nv*Nc,&xfield));
148             for (i=0,cum=0;i<vEnd-vStart;i++) {
149               PetscInt j,off;
150 
151               if (PetscUnlikely(!PetscBTLookup(vown,i))) continue;
152               PetscCall(PetscSectionGetFieldOffset(s,i+vStart,f,&off));
153               for (j=0;j<Nc;j++) idxs[cum++] = off + j;
154             }
155             PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)xlocal),Nv*Nc,idxs,PETSC_USE_POINTER,&isfield));
156           } else {
157             nlocal[ctx->nf] = Nc * totc;
158             PetscCall(PetscStrallocpy(fec,&fec_type[ctx->nf]));
159             PetscCall(VecCreateSeq(PETSC_COMM_SELF,Nc*totc,&xfield));
160             for (i=0,cum=0;i<cEnd-cStart;i++) {
161               PetscInt j,off;
162 
163               if (PetscUnlikely(gNum[i] < 0)) continue;
164               PetscCall(PetscSectionGetFieldOffset(s,i+cStart,f,&off));
165               for (j=0;j<Nc;j++) idxs[cum++] = off + j;
166             }
167             PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)xlocal),totc*Nc,idxs,PETSC_USE_POINTER,&isfield));
168           }
169           PetscCall(VecScatterCreate(xlocal,isfield,xfield,NULL,&ctx->scctx[ctx->nf]));
170           PetscCall(VecDestroy(&xfield));
171           PetscCall(ISDestroy(&isfield));
172           ctx->nf++;
173         } else if (id == PETSCFV_CLASSID) {
174           PetscInt c;
175 
176           PetscCall(PetscFVGetNumComponents((PetscFV)disc,&Nc));
177           PetscCall(PetscSNPrintf(fec,64,"FiniteElementCollection: L2_%DD_P0",dim));
178           for (c = 0; c < Nc; c++) {
179             char comp[256];
180             PetscCall(PetscSNPrintf(comp,256,"%s-Comp%D",name,c));
181             PetscCall(PetscStrallocpy(comp,&fieldname[ctx->nf]));
182             bs[ctx->nf] = 1; /* Does PetscFV support components with different block size? */
183             nlocal[ctx->nf] = totc;
184             dims[ctx->nf] = dim;
185             PetscCall(PetscStrallocpy(fec,&fec_type[ctx->nf]));
186             PetscCall(VecCreateSeq(PETSC_COMM_SELF,totc,&xfield));
187             for (i=0,cum=0;i<cEnd-cStart;i++) {
188               PetscInt off;
189 
190               if (PetscUnlikely(gNum[i])<0) continue;
191               PetscCall(PetscSectionGetFieldOffset(s,i+cStart,f,&off));
192               idxs[cum++] = off + c;
193             }
194             PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)xlocal),totc,idxs,PETSC_USE_POINTER,&isfield));
195             PetscCall(VecScatterCreate(xlocal,isfield,xfield,NULL,&ctx->scctx[ctx->nf]));
196             PetscCall(VecDestroy(&xfield));
197             PetscCall(ISDestroy(&isfield));
198             ctx->nf++;
199           }
200         } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_ARG_WRONG,"Unknown discretization type for field %D",f);
201       } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Missing discretization for field %D",f);
202     }
203   } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Needs a DS attached to the DM");
204   PetscCall(PetscBTDestroy(&vown));
205   PetscCall(VecDestroy(&xlocal));
206   PetscCall(ISRestoreIndices(globalNum,&gNum));
207 
208   /* create work vectors */
209   for (f=0;f<ctx->nf;f++) {
210     PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)dm),nlocal[f],PETSC_DECIDE,&Ufield[f]));
211     PetscCall(PetscObjectSetName((PetscObject)Ufield[f],fieldname[f]));
212     PetscCall(VecSetBlockSize(Ufield[f],bs[f]));
213     PetscCall(VecSetDM(Ufield[f],dm));
214   }
215 
216   /* customize the viewer */
217   PetscCall(PetscViewerGLVisSetFields(viewer,ctx->nf,(const char**)fec_type,dims,DMPlexSampleGLVisFields_Private,(PetscObject*)Ufield,ctx,DestroyGLVisViewerCtx_Private));
218   for (f=0;f<ctx->nf;f++) {
219     PetscCall(PetscFree(fieldname[f]));
220     PetscCall(PetscFree(fec_type[f]));
221     PetscCall(VecDestroy(&Ufield[f]));
222   }
223   PetscCall(PetscFree7(fieldname,nlocal,bs,dims,fec_type,idxs,Ufield));
224   PetscFunctionReturn(0);
225 }
226 
227 typedef enum {MFEM_POINT=0,MFEM_SEGMENT,MFEM_TRIANGLE,MFEM_SQUARE,MFEM_TETRAHEDRON,MFEM_CUBE,MFEM_PRISM,MFEM_UNDEF} MFEM_cid;
228 
229 MFEM_cid mfem_table_cid[4][7]       = { {MFEM_POINT,MFEM_UNDEF,MFEM_UNDEF  ,MFEM_UNDEF   ,MFEM_UNDEF      ,MFEM_UNDEF,MFEM_UNDEF},
230                                         {MFEM_POINT,MFEM_UNDEF,MFEM_SEGMENT,MFEM_UNDEF   ,MFEM_UNDEF      ,MFEM_UNDEF,MFEM_UNDEF},
231                                         {MFEM_POINT,MFEM_UNDEF,MFEM_SEGMENT,MFEM_TRIANGLE,MFEM_SQUARE     ,MFEM_UNDEF,MFEM_UNDEF},
232                                         {MFEM_POINT,MFEM_UNDEF,MFEM_SEGMENT,MFEM_UNDEF   ,MFEM_TETRAHEDRON,MFEM_PRISM,MFEM_CUBE } };
233 
234 MFEM_cid mfem_table_cid_unint[4][9] = { {MFEM_POINT,MFEM_UNDEF,MFEM_UNDEF  ,MFEM_UNDEF   ,MFEM_UNDEF      ,MFEM_UNDEF,MFEM_PRISM,MFEM_UNDEF,MFEM_UNDEF},
235                                         {MFEM_POINT,MFEM_UNDEF,MFEM_SEGMENT,MFEM_UNDEF   ,MFEM_UNDEF      ,MFEM_UNDEF,MFEM_PRISM,MFEM_UNDEF,MFEM_UNDEF},
236                                         {MFEM_POINT,MFEM_UNDEF,MFEM_SEGMENT,MFEM_TRIANGLE,MFEM_SQUARE     ,MFEM_UNDEF,MFEM_PRISM,MFEM_UNDEF,MFEM_UNDEF},
237                                         {MFEM_POINT,MFEM_UNDEF,MFEM_SEGMENT,MFEM_UNDEF   ,MFEM_TETRAHEDRON,MFEM_UNDEF,MFEM_PRISM,MFEM_UNDEF,MFEM_CUBE } };
238 
239 static PetscErrorCode DMPlexGetPointMFEMCellID_Internal(DM dm, DMLabel label, PetscInt minl, PetscInt p, PetscInt *mid, PetscInt *cid)
240 {
241   DMLabel        dlabel;
242   PetscInt       depth,csize,pdepth,dim;
243 
244   PetscFunctionBegin;
245   PetscCall(DMPlexGetDepthLabel(dm,&dlabel));
246   PetscCall(DMLabelGetValue(dlabel,p,&pdepth));
247   PetscCall(DMPlexGetConeSize(dm,p,&csize));
248   PetscCall(DMPlexGetDepth(dm,&depth));
249   PetscCall(DMGetDimension(dm,&dim));
250   if (label) {
251     PetscCall(DMLabelGetValue(label,p,mid));
252     *mid = *mid - minl + 1; /* MFEM does not like negative markers */
253   } else *mid = 1;
254   if (depth >=0 && dim != depth) { /* not interpolated, it assumes cell-vertex mesh */
255     PetscCheckFalse(dim < 0 || dim > 3,PETSC_COMM_SELF,PETSC_ERR_SUP,"Dimension %D",dim);
256     PetscCheck(csize <= 8,PETSC_COMM_SELF,PETSC_ERR_SUP,"Found cone size %D for point %D",csize,p);
257     PetscCheck(depth == 1,PETSC_COMM_SELF,PETSC_ERR_SUP,"Found depth %D for point %D. You should interpolate the mesh first",depth,p);
258     *cid = mfem_table_cid_unint[dim][csize];
259   } else {
260     PetscCheck(csize <= 6,PETSC_COMM_SELF,PETSC_ERR_SUP,"Cone size %D for point %D",csize,p);
261     PetscCheckFalse(pdepth < 0 || pdepth > 3,PETSC_COMM_SELF,PETSC_ERR_SUP,"Depth %D for point %D",csize,p);
262     *cid = mfem_table_cid[pdepth][csize];
263   }
264   PetscFunctionReturn(0);
265 }
266 
267 static PetscErrorCode DMPlexGetPointMFEMVertexIDs_Internal(DM dm, PetscInt p, PetscSection csec, PetscInt *nv, PetscInt vids[])
268 {
269   PetscInt       dim,sdim,dof = 0,off = 0,i,q,vStart,vEnd,numPoints,*points = NULL;
270 
271   PetscFunctionBegin;
272   PetscCall(DMPlexGetDepthStratum(dm,0,&vStart,&vEnd));
273   PetscCall(DMGetDimension(dm,&dim));
274   sdim = dim;
275   if (csec) {
276     PetscInt sStart,sEnd;
277 
278     PetscCall(DMGetCoordinateDim(dm,&sdim));
279     PetscCall(PetscSectionGetChart(csec,&sStart,&sEnd));
280     PetscCall(PetscSectionGetOffset(csec,vStart,&off));
281     off  = off/sdim;
282     if (p >= sStart && p < sEnd) {
283       PetscCall(PetscSectionGetDof(csec,p,&dof));
284     }
285   }
286   if (!dof) {
287     PetscCall(DMPlexGetTransitiveClosure(dm,p,PETSC_TRUE,&numPoints,&points));
288     for (i=0,q=0;i<numPoints*2;i+= 2)
289       if ((points[i] >= vStart) && (points[i] < vEnd))
290         vids[q++] = points[i]-vStart+off;
291     PetscCall(DMPlexRestoreTransitiveClosure(dm,p,PETSC_TRUE,&numPoints,&points));
292   } else {
293     PetscCall(PetscSectionGetOffset(csec,p,&off));
294     PetscCall(PetscSectionGetDof(csec,p,&dof));
295     for (q=0;q<dof/sdim;q++) vids[q] = off/sdim + q;
296   }
297   *nv = q;
298   PetscFunctionReturn(0);
299 }
300 
301 static PetscErrorCode GLVisCreateFE(PetscFE femIn,char name[32],PetscFE *fem,IS *perm)
302 {
303   DM              K;
304   PetscSpace      P;
305   PetscDualSpace  Q;
306   PetscQuadrature q,fq;
307   PetscInt        dim,deg,dof;
308   DMPolytopeType  ptype;
309   PetscBool       isSimplex,isTensor;
310   PetscBool       continuity = PETSC_FALSE;
311   PetscDTNodeType nodeType   = PETSCDTNODES_GAUSSJACOBI;
312   PetscBool       endpoint   = PETSC_TRUE;
313   MPI_Comm        comm;
314 
315   PetscFunctionBegin;
316   PetscCall(PetscObjectGetComm((PetscObject)femIn, &comm));
317   PetscCall(PetscFEGetBasisSpace(femIn,&P));
318   PetscCall(PetscFEGetDualSpace(femIn,&Q));
319   PetscCall(PetscDualSpaceGetDM(Q,&K));
320   PetscCall(DMGetDimension(K,&dim));
321   PetscCall(PetscSpaceGetDegree(P,&deg,NULL));
322   PetscCall(PetscSpaceGetNumComponents(P,&dof));
323   PetscCall(DMPlexGetCellType(K,0,&ptype));
324   switch (ptype) {
325   case DM_POLYTOPE_QUADRILATERAL:
326   case DM_POLYTOPE_HEXAHEDRON:
327     isSimplex = PETSC_FALSE; break;
328   default:
329     isSimplex = PETSC_TRUE; break;
330   }
331   isTensor = isSimplex ? PETSC_FALSE : PETSC_TRUE;
332   if (isSimplex) deg = PetscMin(deg,3); /* Permutation not coded for degree higher than 3 */
333   /* Create space */
334   PetscCall(PetscSpaceCreate(comm,&P));
335   PetscCall(PetscSpaceSetType(P,PETSCSPACEPOLYNOMIAL));
336   PetscCall(PetscSpacePolynomialSetTensor(P,isTensor));
337   PetscCall(PetscSpaceSetNumComponents(P,dof));
338   PetscCall(PetscSpaceSetNumVariables(P,dim));
339   PetscCall(PetscSpaceSetDegree(P,deg,PETSC_DETERMINE));
340   PetscCall(PetscSpaceSetUp(P));
341   /* Create dual space */
342   PetscCall(PetscDualSpaceCreate(comm,&Q));
343   PetscCall(PetscDualSpaceSetType(Q,PETSCDUALSPACELAGRANGE));
344   PetscCall(PetscDualSpaceLagrangeSetTensor(Q,isTensor));
345   PetscCall(PetscDualSpaceLagrangeSetContinuity(Q,continuity));
346   PetscCall(PetscDualSpaceLagrangeSetNodeType(Q,nodeType,endpoint,0));
347   PetscCall(PetscDualSpaceSetNumComponents(Q,dof));
348   PetscCall(PetscDualSpaceSetOrder(Q,deg));
349   PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, DMPolytopeTypeSimpleShape(dim, isSimplex), &K));
350   PetscCall(PetscDualSpaceSetDM(Q,K));
351   PetscCall(DMDestroy(&K));
352   PetscCall(PetscDualSpaceSetUp(Q));
353   /* Create quadrature */
354   if (isSimplex) {
355     PetscCall(PetscDTStroudConicalQuadrature(dim,  1,deg+1,-1,+1,&q));
356     PetscCall(PetscDTStroudConicalQuadrature(dim-1,1,deg+1,-1,+1,&fq));
357   } else {
358     PetscCall(PetscDTGaussTensorQuadrature(dim,  1,deg+1,-1,+1,&q));
359     PetscCall(PetscDTGaussTensorQuadrature(dim-1,1,deg+1,-1,+1,&fq));
360   }
361   /* Create finite element */
362   PetscCall(PetscFECreate(comm,fem));
363   PetscCall(PetscSNPrintf(name,32,"L2_T1_%DD_P%D",dim,deg));
364   PetscCall(PetscObjectSetName((PetscObject)*fem,name));
365   PetscCall(PetscFESetType(*fem,PETSCFEBASIC));
366   PetscCall(PetscFESetNumComponents(*fem,dof));
367   PetscCall(PetscFESetBasisSpace(*fem,P));
368   PetscCall(PetscFESetDualSpace(*fem,Q));
369   PetscCall(PetscFESetQuadrature(*fem,q));
370   PetscCall(PetscFESetFaceQuadrature(*fem,fq));
371   PetscCall(PetscFESetUp(*fem));
372 
373   /* Both MFEM and PETSc are lexicographic, but PLEX stores the swapped cone */
374   *perm = NULL;
375   if (isSimplex && dim == 3) {
376     PetscInt celldofs,*pidx;
377 
378     PetscCall(PetscDualSpaceGetDimension(Q,&celldofs));
379     celldofs /= dof;
380     PetscCall(PetscMalloc1(celldofs,&pidx));
381     switch (celldofs) {
382     case 4:
383       pidx[0] = 2;
384       pidx[1] = 0;
385       pidx[2] = 1;
386       pidx[3] = 3;
387     break;
388     case 10:
389       pidx[0] = 5;
390       pidx[1] = 3;
391       pidx[2] = 0;
392       pidx[3] = 4;
393       pidx[4] = 1;
394       pidx[5] = 2;
395       pidx[6] = 8;
396       pidx[7] = 6;
397       pidx[8] = 7;
398       pidx[9] = 9;
399     break;
400     case 20:
401       pidx[ 0] = 9;
402       pidx[ 1] = 7;
403       pidx[ 2] = 4;
404       pidx[ 3] = 0;
405       pidx[ 4] = 8;
406       pidx[ 5] = 5;
407       pidx[ 6] = 1;
408       pidx[ 7] = 6;
409       pidx[ 8] = 2;
410       pidx[ 9] = 3;
411       pidx[10] = 15;
412       pidx[11] = 13;
413       pidx[12] = 10;
414       pidx[13] = 14;
415       pidx[14] = 11;
416       pidx[15] = 12;
417       pidx[16] = 18;
418       pidx[17] = 16;
419       pidx[18] = 17;
420       pidx[19] = 19;
421     break;
422     default:
423       SETERRQ(comm,PETSC_ERR_SUP,"Unhandled degree,dof pair %D,%D",deg,celldofs);
424     break;
425     }
426     PetscCall(ISCreateBlock(PETSC_COMM_SELF,dof,celldofs,pidx,PETSC_OWN_POINTER,perm));
427   }
428 
429   /* Cleanup */
430   PetscCall(PetscSpaceDestroy(&P));
431   PetscCall(PetscDualSpaceDestroy(&Q));
432   PetscCall(PetscQuadratureDestroy(&q));
433   PetscCall(PetscQuadratureDestroy(&fq));
434   PetscFunctionReturn(0);
435 }
436 
437 /*
438    ASCII visualization/dump: full support for simplices and tensor product cells. It supports AMR
439    Higher order meshes are also supported
440 */
441 static PetscErrorCode DMPlexView_GLVis_ASCII(DM dm, PetscViewer viewer)
442 {
443   DMLabel              label;
444   PetscSection         coordSection,parentSection,hoSection = NULL;
445   Vec                  coordinates,hovec;
446   const PetscScalar    *array;
447   PetscInt             bf,p,sdim,dim,depth,novl,minl;
448   PetscInt             cStart,cEnd,vStart,vEnd,nvert;
449   PetscMPIInt          size;
450   PetscBool            localized,isascii;
451   PetscBool            enable_mfem,enable_boundary,enable_ncmesh,view_ovl = PETSC_FALSE;
452   PetscBT              pown,vown;
453   PetscContainer       glvis_container;
454   PetscBool            cellvertex = PETSC_FALSE, periodic, enabled = PETSC_TRUE;
455   PetscBool            enable_emark,enable_bmark;
456   const char           *fmt;
457   char                 emark[64] = "",bmark[64] = "";
458 
459   PetscFunctionBegin;
460   PetscValidHeaderSpecific(dm,DM_CLASSID,1);
461   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
462   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
463   PetscCheck(isascii,PetscObjectComm((PetscObject)viewer),PETSC_ERR_SUP,"Viewer must be of type VIEWERASCII");
464   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)viewer),&size));
465   PetscCheck(size <= 1,PetscObjectComm((PetscObject)viewer),PETSC_ERR_SUP,"Use single sequential viewers for parallel visualization");
466   PetscCall(DMGetDimension(dm,&dim));
467   PetscCall(DMPlexGetDepth(dm,&depth));
468 
469   /* get container: determines if a process visualizes is portion of the data or not */
470   PetscCall(PetscObjectQuery((PetscObject)viewer,"_glvis_info_container",(PetscObject*)&glvis_container));
471   PetscCheck(glvis_container,PetscObjectComm((PetscObject)dm),PETSC_ERR_PLIB,"Missing GLVis container");
472   {
473     PetscViewerGLVisInfo glvis_info;
474     PetscCall(PetscContainerGetPointer(glvis_container,(void**)&glvis_info));
475     enabled = glvis_info->enabled;
476     fmt     = glvis_info->fmt;
477   }
478 
479   /* Users can attach a coordinate vector to the DM in case they have a higher-order mesh */
480   PetscCall(PetscObjectQuery((PetscObject)dm,"_glvis_mesh_coords",(PetscObject*)&hovec));
481   PetscCall(PetscObjectReference((PetscObject)hovec));
482   if (!hovec) {
483     DM           cdm;
484     PetscFE      disc;
485     PetscClassId classid;
486 
487     PetscCall(DMGetCoordinateDM(dm,&cdm));
488     PetscCall(DMGetField(cdm,0,NULL,(PetscObject*)&disc));
489     PetscCall(PetscObjectGetClassId((PetscObject)disc,&classid));
490     if (classid == PETSCFE_CLASSID) {
491       DM      hocdm;
492       PetscFE hodisc;
493       Vec     vec;
494       Mat     mat;
495       char    name[32],fec_type[64];
496       IS      perm = NULL;
497 
498       PetscCall(GLVisCreateFE(disc,name,&hodisc,&perm));
499       PetscCall(DMClone(cdm,&hocdm));
500       PetscCall(DMSetField(hocdm,0,NULL,(PetscObject)hodisc));
501       PetscCall(PetscFEDestroy(&hodisc));
502       PetscCall(DMCreateDS(hocdm));
503 
504       PetscCall(DMGetCoordinates(dm,&vec));
505       PetscCall(DMCreateGlobalVector(hocdm,&hovec));
506       PetscCall(DMCreateInterpolation(cdm,hocdm,&mat,NULL));
507       PetscCall(MatInterpolate(mat,vec,hovec));
508       PetscCall(MatDestroy(&mat));
509       PetscCall(DMGetLocalSection(hocdm,&hoSection));
510       PetscCall(PetscSectionSetClosurePermutation(hoSection, (PetscObject)hocdm, depth, perm));
511       PetscCall(ISDestroy(&perm));
512       PetscCall(DMDestroy(&hocdm));
513       PetscCall(PetscSNPrintf(fec_type,sizeof(fec_type),"FiniteElementCollection: %s", name));
514       PetscCall(PetscObjectSetName((PetscObject)hovec,fec_type));
515     }
516   }
517 
518   PetscCall(DMPlexGetHeightStratum(dm,0,&cStart,&cEnd));
519   PetscCall(DMPlexGetGhostCellStratum(dm,&p,NULL));
520   if (p >= 0) cEnd = p;
521   PetscCall(DMPlexGetDepthStratum(dm,0,&vStart,&vEnd));
522   PetscCall(DMGetPeriodicity(dm,&periodic,NULL,NULL,NULL));
523   PetscCall(DMGetCoordinatesLocalized(dm,&localized));
524   PetscCheckFalse(periodic && !localized && !hovec,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Coordinates need to be localized");
525   PetscCall(DMGetCoordinateSection(dm,&coordSection));
526   PetscCall(DMGetCoordinateDim(dm,&sdim));
527   PetscCall(DMGetCoordinatesLocal(dm,&coordinates));
528   PetscCheck(coordinates || hovec,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Missing local coordinates vector");
529 
530   /*
531      a couple of sections of the mesh specification are disabled
532        - 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)
533        - vertex_parents: used for non-conforming meshes only when we want to use MFEM as a discretization package
534                          and be able to derefine the mesh (MFEM does not currently have to ability to read ncmeshes in parallel)
535   */
536   enable_boundary = PETSC_FALSE;
537   enable_ncmesh   = PETSC_FALSE;
538   enable_mfem     = PETSC_FALSE;
539   enable_emark    = PETSC_FALSE;
540   enable_bmark    = PETSC_FALSE;
541   /* I'm tired of problems with negative values in the markers, disable them */
542   PetscOptionsBegin(PetscObjectComm((PetscObject)dm),((PetscObject)dm)->prefix,"GLVis PetscViewer DMPlex Options","PetscViewer");
543   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_enable_boundary","Enable boundary section in mesh representation",NULL,enable_boundary,&enable_boundary,NULL));
544   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_enable_ncmesh","Enable vertex_parents section in mesh representation (allows derefinement)",NULL,enable_ncmesh,&enable_ncmesh,NULL));
545   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));
546   PetscCall(PetscOptionsBool("-viewer_glvis_dm_plex_overlap","Include overlap region in local meshes",NULL,view_ovl,&view_ovl,NULL));
547   PetscCall(PetscOptionsString("-viewer_glvis_dm_plex_emarker","String for the material id label",NULL,emark,emark,sizeof(emark),&enable_emark));
548   PetscCall(PetscOptionsString("-viewer_glvis_dm_plex_bmarker","String for the boundary id label",NULL,bmark,bmark,sizeof(bmark),&enable_bmark));
549   PetscOptionsEnd();
550   if (enable_bmark) enable_boundary = PETSC_TRUE;
551 
552   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm),&size));
553   PetscCheckFalse(enable_ncmesh && size > 1,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Not supported in parallel");
554   PetscCheckFalse(enable_boundary && depth >= 0 && dim != depth,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Mesh must be interpolated. "
555                                                              "Alternatively, run with -viewer_glvis_dm_plex_enable_boundary 0");
556   PetscCheckFalse(enable_ncmesh && depth >= 0 && dim != depth,PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Mesh must be interpolated. "
557                                                            "Alternatively, run with -viewer_glvis_dm_plex_enable_ncmesh 0");
558   if (depth >=0 && dim != depth) { /* not interpolated, it assumes cell-vertex mesh */
559     PetscCheck(depth == 1,PETSC_COMM_SELF,PETSC_ERR_SUP,"Unsupported depth %D. You should interpolate the mesh first",depth);
560     cellvertex = PETSC_TRUE;
561   }
562 
563   /* Identify possible cells in the overlap */
564   novl = 0;
565   pown = NULL;
566   if (size > 1) {
567     IS             globalNum = NULL;
568     const PetscInt *gNum;
569     PetscBool      ovl  = PETSC_FALSE;
570 
571     PetscCall(PetscObjectQuery((PetscObject)dm,"_glvis_plex_gnum",(PetscObject*)&globalNum));
572     if (!globalNum) {
573       if (view_ovl) {
574         PetscCall(ISCreateStride(PetscObjectComm((PetscObject)dm),cEnd-cStart,0,1,&globalNum));
575       } else {
576         PetscCall(DMPlexCreateCellNumbering_Internal(dm,PETSC_TRUE,&globalNum));
577       }
578       PetscCall(PetscObjectCompose((PetscObject)dm,"_glvis_plex_gnum",(PetscObject)globalNum));
579       PetscCall(PetscObjectDereference((PetscObject)globalNum));
580     }
581     PetscCall(ISGetIndices(globalNum,&gNum));
582     for (p=cStart; p<cEnd; p++) {
583       if (gNum[p-cStart] < 0) {
584         ovl = PETSC_TRUE;
585         novl++;
586       }
587     }
588     if (ovl) {
589       /* it may happen that pown get not destroyed, if the user closes the window while this function is running.
590          TODO: garbage collector? attach pown to dm?  */
591       PetscCall(PetscBTCreate(cEnd-cStart,&pown));
592       for (p=cStart; p<cEnd; p++) {
593         if (gNum[p-cStart] < 0) continue;
594         else {
595           PetscCall(PetscBTSet(pown,p-cStart));
596         }
597       }
598     }
599     PetscCall(ISRestoreIndices(globalNum,&gNum));
600   }
601 
602   /* vertex_parents (Non-conforming meshes) */
603   parentSection  = NULL;
604   if (enable_ncmesh) {
605     PetscCall(DMPlexGetTree(dm,&parentSection,NULL,NULL,NULL,NULL));
606     enable_ncmesh = (PetscBool)(enable_ncmesh && parentSection);
607   }
608   /* return if this process is disabled */
609   if (!enabled) {
610     PetscCall(PetscViewerASCIIPrintf(viewer,"MFEM mesh %s\n",enable_ncmesh ? "v1.1" : "v1.0"));
611     PetscCall(PetscViewerASCIIPrintf(viewer,"\ndimension\n"));
612     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",dim));
613     PetscCall(PetscViewerASCIIPrintf(viewer,"\nelements\n"));
614     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",0));
615     PetscCall(PetscViewerASCIIPrintf(viewer,"\nboundary\n"));
616     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",0));
617     PetscCall(PetscViewerASCIIPrintf(viewer,"\nvertices\n"));
618     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",0));
619     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",sdim));
620     PetscCall(PetscBTDestroy(&pown));
621     PetscCall(VecDestroy(&hovec));
622     PetscFunctionReturn(0);
623   }
624 
625   if (enable_mfem) {
626     if (periodic && !hovec) { /* we need to generate a vector of L2 coordinates, as this is how MFEM handles periodic meshes */
627       PetscInt    vpc = 0;
628       char        fec[64];
629       PetscInt    vids[8] = {0,1,2,3,4,5,6,7};
630       PetscInt    hexv[8] = {0,1,3,2,4,5,7,6}, tetv[4] = {0,1,2,3};
631       PetscInt    quadv[8] = {0,1,3,2}, triv[3] = {0,1,2};
632       PetscInt    *dof = NULL;
633       PetscScalar *array,*ptr;
634 
635       PetscCall(PetscSNPrintf(fec,sizeof(fec),"FiniteElementCollection: L2_T1_%DD_P1",dim));
636       if (cEnd-cStart) {
637         PetscInt fpc;
638 
639         PetscCall(DMPlexGetConeSize(dm,cStart,&fpc));
640         switch(dim) {
641           case 1:
642             vpc = 2;
643             dof = hexv;
644             break;
645           case 2:
646             switch (fpc) {
647               case 3:
648                 vpc = 3;
649                 dof = triv;
650                 break;
651               case 4:
652                 vpc = 4;
653                 dof = quadv;
654                 break;
655               default:
656                 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unhandled case: faces per cell %D",fpc);
657             }
658             break;
659           case 3:
660             switch (fpc) {
661               case 4: /* TODO: still need to understand L2 ordering for tets */
662                 vpc = 4;
663                 dof = tetv;
664                 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unhandled tethraedral case");
665               case 6:
666                 PetscCheck(!cellvertex,PETSC_COMM_SELF,PETSC_ERR_SUP,"Unhandled case: vertices per cell %D",fpc);
667                 vpc = 8;
668                 dof = hexv;
669                 break;
670               case 8:
671                 PetscCheck(cellvertex,PETSC_COMM_SELF,PETSC_ERR_SUP,"Unhandled case: faces per cell %D",fpc);
672                 vpc = 8;
673                 dof = hexv;
674                 break;
675               default:
676                 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Unhandled case: faces per cell %D",fpc);
677             }
678             break;
679           default:
680             SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Unhandled dim");
681         }
682         PetscCall(DMPlexReorderCell(dm,cStart,vids));
683       }
684       PetscCheck(dof,PetscObjectComm((PetscObject)dm),PETSC_ERR_PLIB,"Missing dofs");
685       PetscCall(VecCreateSeq(PETSC_COMM_SELF,(cEnd-cStart-novl)*vpc*sdim,&hovec));
686       PetscCall(PetscObjectSetName((PetscObject)hovec,fec));
687       PetscCall(VecGetArray(hovec,&array));
688       ptr  = array;
689       for (p=cStart;p<cEnd;p++) {
690         PetscInt    csize,v,d;
691         PetscScalar *vals = NULL;
692 
693         if (PetscUnlikely(pown && !PetscBTLookup(pown,p-cStart))) continue;
694         PetscCall(DMPlexVecGetClosure(dm,coordSection,coordinates,p,&csize,&vals));
695         PetscCheckFalse(csize != vpc*sdim && csize != vpc*sdim*2,PETSC_COMM_SELF,PETSC_ERR_SUP,"Unsupported closure size %D (vpc %D, sdim %D)",csize,vpc,sdim);
696         for (v=0;v<vpc;v++) {
697           for (d=0;d<sdim;d++) {
698             ptr[sdim*dof[v]+d] = vals[sdim*vids[v]+d];
699           }
700         }
701         ptr += vpc*sdim;
702         PetscCall(DMPlexVecRestoreClosure(dm,coordSection,coordinates,p,&csize,&vals));
703       }
704       PetscCall(VecRestoreArray(hovec,&array));
705     }
706   }
707   /* if we have high-order coordinates in 3D, we need to specify the boundary */
708   if (hovec && dim == 3) enable_boundary = PETSC_TRUE;
709 
710   /* header */
711   PetscCall(PetscViewerASCIIPrintf(viewer,"MFEM mesh %s\n",enable_ncmesh ? "v1.1" : "v1.0"));
712 
713   /* topological dimension */
714   PetscCall(PetscViewerASCIIPrintf(viewer,"\ndimension\n"));
715   PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",dim));
716 
717   /* elements */
718   minl = 1;
719   label = NULL;
720   if (enable_emark) {
721     PetscInt lminl = PETSC_MAX_INT;
722 
723     PetscCall(DMGetLabel(dm,emark,&label));
724     if (label) {
725       IS       vals;
726       PetscInt ldef;
727 
728       PetscCall(DMLabelGetDefaultValue(label,&ldef));
729       PetscCall(DMLabelGetValueIS(label,&vals));
730       PetscCall(ISGetMinMax(vals,&lminl,NULL));
731       PetscCall(ISDestroy(&vals));
732       lminl = PetscMin(ldef,lminl);
733     }
734     PetscCall(MPIU_Allreduce(&lminl,&minl,1,MPIU_INT,MPI_MIN,PetscObjectComm((PetscObject)dm)));
735     if (minl == PETSC_MAX_INT) minl = 1;
736   }
737   PetscCall(PetscViewerASCIIPrintf(viewer,"\nelements\n"));
738   PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",cEnd-cStart-novl));
739   for (p=cStart;p<cEnd;p++) {
740     PetscInt       vids[8];
741     PetscInt       i,nv = 0,cid = -1,mid = 1;
742 
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,"%D %D",mid,cid));
748     for (i=0;i<nv;i++) {
749       PetscCall(PetscViewerASCIIPrintf(viewer," %D",vids[i]));
750     }
751     PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
752   }
753 
754   /* boundary */
755   PetscCall(PetscViewerASCIIPrintf(viewer,"\nboundary\n"));
756   if (!enable_boundary) {
757     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",0));
758   } else {
759     DMLabel  perLabel;
760     PetscBT  bfaces;
761     PetscInt fStart,fEnd,*fcells;
762 
763     PetscCall(DMPlexGetHeightStratum(dm,1,&fStart,&fEnd));
764     PetscCall(PetscBTCreate(fEnd-fStart,&bfaces));
765     PetscCall(DMPlexGetMaxSizes(dm,NULL,&p));
766     PetscCall(PetscMalloc1(p,&fcells));
767     PetscCall(DMGetLabel(dm,"glvis_periodic_cut",&perLabel));
768     if (!perLabel && periodic) { /* this periodic cut can be moved up to DMPlex setup */
769       PetscCall(DMCreateLabel(dm,"glvis_periodic_cut"));
770       PetscCall(DMGetLabel(dm,"glvis_periodic_cut",&perLabel));
771       PetscCall(DMLabelSetDefaultValue(perLabel,1));
772       for (p=cStart;p<cEnd;p++) {
773         DMPolytopeType cellType;
774         PetscInt       dof;
775 
776         PetscCall(DMPlexGetCellType(dm,p,&cellType));
777         PetscCall(PetscSectionGetDof(coordSection,p,&dof));
778         if (dof) {
779           PetscInt    uvpc, v,csize,cellClosureSize,*cellClosure = NULL,*vidxs = NULL;
780           PetscScalar *vals = NULL;
781 
782           uvpc = DMPolytopeTypeGetNumVertices(cellType);
783           PetscCheck(dof%sdim == 0,PETSC_COMM_SELF,PETSC_ERR_USER,"Incompatible number of cell dofs %D and space dimension %D",dof,sdim);
784           PetscCall(DMPlexVecGetClosure(dm,coordSection,coordinates,p,&csize,&vals));
785           PetscCall(DMPlexGetTransitiveClosure(dm,p,PETSC_TRUE,&cellClosureSize,&cellClosure));
786           for (v=0;v<cellClosureSize;v++)
787             if (cellClosure[2*v] >= vStart && cellClosure[2*v] < vEnd) {
788               vidxs = cellClosure + 2*v;
789               break;
790             }
791           PetscCheck(vidxs,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Missing vertices");
792           for (v=0;v<uvpc;v++) {
793             PetscInt s;
794 
795             for (s=0;s<sdim;s++) {
796               if (PetscAbsScalar(vals[v*sdim+s]-vals[v*sdim+s+uvpc*sdim])>PETSC_MACHINE_EPSILON) {
797                 PetscCall(DMLabelSetValue(perLabel,vidxs[2*v],2));
798               }
799             }
800           }
801           PetscCall(DMPlexRestoreTransitiveClosure(dm,p,PETSC_TRUE,&cellClosureSize,&cellClosure));
802           PetscCall(DMPlexVecRestoreClosure(dm,coordSection,coordinates,p,&csize,&vals));
803         }
804       }
805       if (dim > 1) {
806         PetscInt eEnd,eStart;
807 
808         PetscCall(DMPlexGetDepthStratum(dm,1,&eStart,&eEnd));
809         for (p=eStart;p<eEnd;p++) {
810           const PetscInt *cone;
811           PetscInt       coneSize,i;
812           PetscBool      ispe = PETSC_TRUE;
813 
814           PetscCall(DMPlexGetCone(dm,p,&cone));
815           PetscCall(DMPlexGetConeSize(dm,p,&coneSize));
816           for (i=0;i<coneSize;i++) {
817             PetscInt v;
818 
819             PetscCall(DMLabelGetValue(perLabel,cone[i],&v));
820             ispe = (PetscBool)(ispe && (v==2));
821           }
822           if (ispe && coneSize) {
823             PetscInt       ch, numChildren;
824             const PetscInt *children;
825 
826             PetscCall(DMLabelSetValue(perLabel,p,2));
827             PetscCall(DMPlexGetTreeChildren(dm,p,&numChildren,&children));
828             for (ch = 0; ch < numChildren; ch++) {
829               PetscCall(DMLabelSetValue(perLabel,children[ch],2));
830             }
831           }
832         }
833         if (dim > 2) {
834           for (p=fStart;p<fEnd;p++) {
835             const PetscInt *cone;
836             PetscInt       coneSize,i;
837             PetscBool      ispe = PETSC_TRUE;
838 
839             PetscCall(DMPlexGetCone(dm,p,&cone));
840             PetscCall(DMPlexGetConeSize(dm,p,&coneSize));
841             for (i=0;i<coneSize;i++) {
842               PetscInt v;
843 
844               PetscCall(DMLabelGetValue(perLabel,cone[i],&v));
845               ispe = (PetscBool)(ispe && (v==2));
846             }
847             if (ispe && coneSize) {
848               PetscInt       ch, numChildren;
849               const PetscInt *children;
850 
851               PetscCall(DMLabelSetValue(perLabel,p,2));
852               PetscCall(DMPlexGetTreeChildren(dm,p,&numChildren,&children));
853               for (ch = 0; ch < numChildren; ch++) {
854                 PetscCall(DMLabelSetValue(perLabel,children[ch],2));
855               }
856             }
857           }
858         }
859       }
860     }
861     for (p=fStart;p<fEnd;p++) {
862       const PetscInt *support;
863       PetscInt       supportSize;
864       PetscBool      isbf = PETSC_FALSE;
865 
866       PetscCall(DMPlexGetSupportSize(dm,p,&supportSize));
867       if (pown) {
868         PetscBool has_owned = PETSC_FALSE, has_ghost = PETSC_FALSE;
869         PetscInt  i;
870 
871         PetscCall(DMPlexGetSupport(dm,p,&support));
872         for (i=0;i<supportSize;i++) {
873           if (PetscLikely(PetscBTLookup(pown,support[i]-cStart))) has_owned = PETSC_TRUE;
874           else has_ghost = PETSC_TRUE;
875         }
876         isbf = (PetscBool)((supportSize == 1 && has_owned) || (supportSize > 1 && has_owned && has_ghost));
877       } else {
878         isbf = (PetscBool)(supportSize == 1);
879       }
880       if (!isbf && perLabel) {
881         const PetscInt *cone;
882         PetscInt       coneSize,i;
883 
884         PetscCall(DMPlexGetCone(dm,p,&cone));
885         PetscCall(DMPlexGetConeSize(dm,p,&coneSize));
886         isbf = PETSC_TRUE;
887         for (i=0;i<coneSize;i++) {
888           PetscInt v,d;
889 
890           PetscCall(DMLabelGetValue(perLabel,cone[i],&v));
891           PetscCall(DMLabelGetDefaultValue(perLabel,&d));
892           isbf = (PetscBool)(isbf && v != d);
893         }
894       }
895       if (isbf) {
896         PetscCall(PetscBTSet(bfaces,p-fStart));
897       }
898     }
899     /* count boundary faces */
900     for (p=fStart,bf=0;p<fEnd;p++) {
901       if (PetscUnlikely(PetscBTLookup(bfaces,p-fStart))) {
902         const PetscInt *support;
903         PetscInt       supportSize,c;
904 
905         PetscCall(DMPlexGetSupportSize(dm,p,&supportSize));
906         PetscCall(DMPlexGetSupport(dm,p,&support));
907         for (c=0;c<supportSize;c++) {
908           const    PetscInt *cone;
909           PetscInt cell,cl,coneSize;
910 
911           cell = support[c];
912           if (pown && PetscUnlikely(!PetscBTLookup(pown,cell-cStart))) continue;
913           PetscCall(DMPlexGetCone(dm,cell,&cone));
914           PetscCall(DMPlexGetConeSize(dm,cell,&coneSize));
915           for (cl=0;cl<coneSize;cl++) {
916             if (cone[cl] == p) {
917               bf += 1;
918               break;
919             }
920           }
921         }
922       }
923     }
924     minl = 1;
925     label = NULL;
926     if (enable_bmark) {
927       PetscInt lminl = PETSC_MAX_INT;
928 
929       PetscCall(DMGetLabel(dm,bmark,&label));
930       if (label) {
931         IS       vals;
932         PetscInt ldef;
933 
934         PetscCall(DMLabelGetDefaultValue(label,&ldef));
935         PetscCall(DMLabelGetValueIS(label,&vals));
936         PetscCall(ISGetMinMax(vals,&lminl,NULL));
937         PetscCall(ISDestroy(&vals));
938         lminl = PetscMin(ldef,lminl);
939       }
940       PetscCall(MPIU_Allreduce(&lminl,&minl,1,MPIU_INT,MPI_MIN,PetscObjectComm((PetscObject)dm)));
941       if (minl == PETSC_MAX_INT) minl = 1;
942     }
943     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",bf));
944     for (p=fStart;p<fEnd;p++) {
945       if (PetscUnlikely(PetscBTLookup(bfaces,p-fStart))) {
946         const PetscInt *support;
947         PetscInt       supportSize,c,nc = 0;
948 
949         PetscCall(DMPlexGetSupportSize(dm,p,&supportSize));
950         PetscCall(DMPlexGetSupport(dm,p,&support));
951         if (pown) {
952           for (c=0;c<supportSize;c++) {
953             if (PetscLikely(PetscBTLookup(pown,support[c]-cStart))) {
954               fcells[nc++] = support[c];
955             }
956           }
957         } else for (c=0;c<supportSize;c++) fcells[nc++] = support[c];
958         for (c=0;c<nc;c++) {
959           const DMPolytopeType *faceTypes;
960           DMPolytopeType       cellType;
961           const PetscInt       *faceSizes,*cone;
962           PetscInt             vids[8],*faces,st,i,coneSize,cell,cl,nv,cid = -1,mid = -1;
963 
964           cell = fcells[c];
965           PetscCall(DMPlexGetCone(dm,cell,&cone));
966           PetscCall(DMPlexGetConeSize(dm,cell,&coneSize));
967           for (cl=0;cl<coneSize;cl++)
968             if (cone[cl] == p)
969               break;
970           if (cl == coneSize) continue;
971 
972           /* face material id and type */
973           PetscCall(DMPlexGetPointMFEMCellID_Internal(dm,label,minl,p,&mid,&cid));
974           PetscCall(PetscViewerASCIIPrintf(viewer,"%D %D",mid,cid));
975           /* vertex ids */
976           PetscCall(DMPlexGetCellType(dm,cell,&cellType));
977           PetscCall(DMPlexGetPointMFEMVertexIDs_Internal(dm,cell,(localized && !hovec) ? coordSection : NULL,&nv,vids));
978           PetscCall(DMPlexGetRawFaces_Internal(dm,cellType,vids,NULL,&faceTypes,&faceSizes,(const PetscInt**)&faces));
979           st = 0;
980           for (i=0;i<cl;i++) st += faceSizes[i];
981           PetscCall(DMPlexInvertCell(faceTypes[cl],faces + st));
982           for (i=0;i<faceSizes[cl];i++) {
983             PetscCall(PetscViewerASCIIPrintf(viewer," %d",faces[st+i]));
984           }
985           PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
986           PetscCall(DMPlexRestoreRawFaces_Internal(dm,cellType,vids,NULL,&faceTypes,&faceSizes,(const PetscInt**)&faces));
987           bf -= 1;
988         }
989       }
990     }
991     PetscCheck(!bf,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Remaining boundary faces %D",bf);
992     PetscCall(PetscBTDestroy(&bfaces));
993     PetscCall(PetscFree(fcells));
994   }
995 
996   /* mark owned vertices */
997   vown = NULL;
998   if (pown) {
999     PetscCall(PetscBTCreate(vEnd-vStart,&vown));
1000     for (p=cStart;p<cEnd;p++) {
1001       PetscInt i,closureSize,*closure = NULL;
1002 
1003       if (PetscUnlikely(!PetscBTLookup(pown,p-cStart))) continue;
1004       PetscCall(DMPlexGetTransitiveClosure(dm,p,PETSC_TRUE,&closureSize,&closure));
1005       for (i=0;i<closureSize;i++) {
1006         const PetscInt pp = closure[2*i];
1007 
1008         if (pp >= vStart && pp < vEnd) {
1009           PetscCall(PetscBTSet(vown,pp-vStart));
1010         }
1011       }
1012       PetscCall(DMPlexRestoreTransitiveClosure(dm,p,PETSC_TRUE,&closureSize,&closure));
1013     }
1014   }
1015 
1016   if (parentSection) {
1017     PetscInt vp,gvp;
1018 
1019     for (vp=0,p=vStart;p<vEnd;p++) {
1020       DMLabel  dlabel;
1021       PetscInt parent,depth;
1022 
1023       if (PetscUnlikely(vown && !PetscBTLookup(vown,p-vStart))) continue;
1024       PetscCall(DMPlexGetDepthLabel(dm,&dlabel));
1025       PetscCall(DMLabelGetValue(dlabel,p,&depth));
1026       PetscCall(DMPlexGetTreeParent(dm,p,&parent,NULL));
1027       if (parent != p) vp++;
1028     }
1029     PetscCall(MPIU_Allreduce(&vp,&gvp,1,MPIU_INT,MPI_SUM,PetscObjectComm((PetscObject)dm)));
1030     if (gvp) {
1031       PetscInt  maxsupp;
1032       PetscBool *skip = NULL;
1033 
1034       PetscCall(PetscViewerASCIIPrintf(viewer,"\nvertex_parents\n"));
1035       PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",vp));
1036       PetscCall(DMPlexGetMaxSizes(dm,NULL,&maxsupp));
1037       PetscCall(PetscMalloc1(maxsupp,&skip));
1038       for (p=vStart;p<vEnd;p++) {
1039         DMLabel  dlabel;
1040         PetscInt parent;
1041 
1042         if (PetscUnlikely(vown && !PetscBTLookup(vown,p-vStart))) continue;
1043         PetscCall(DMPlexGetDepthLabel(dm,&dlabel));
1044         PetscCall(DMPlexGetTreeParent(dm,p,&parent,NULL));
1045         if (parent != p) {
1046           PetscInt       vids[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; /* silent overzealous clang static analyzer */
1047           PetscInt       i,nv,ssize,n,numChildren,depth = -1;
1048           const PetscInt *children;
1049 
1050           PetscCall(DMPlexGetConeSize(dm,parent,&ssize));
1051           switch (ssize) {
1052             case 2: /* edge */
1053               nv   = 0;
1054               PetscCall(DMPlexGetPointMFEMVertexIDs_Internal(dm,parent,localized ? coordSection : NULL,&nv,vids));
1055               PetscCall(PetscViewerASCIIPrintf(viewer,"%D",p-vStart));
1056               for (i=0;i<nv;i++) {
1057                 PetscCall(PetscViewerASCIIPrintf(viewer," %D",vids[i]));
1058               }
1059               PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
1060               vp--;
1061               break;
1062             case 4: /* face */
1063               PetscCall(DMPlexGetTreeChildren(dm,parent,&numChildren,&children));
1064               for (n=0;n<numChildren;n++) {
1065                 PetscCall(DMLabelGetValue(dlabel,children[n],&depth));
1066                 if (!depth) {
1067                   const PetscInt *hvsupp,*hesupp,*cone;
1068                   PetscInt       hvsuppSize,hesuppSize,coneSize;
1069                   PetscInt       hv = children[n],he = -1,f;
1070 
1071                   PetscCall(PetscArrayzero(skip,maxsupp));
1072                   PetscCall(DMPlexGetSupportSize(dm,hv,&hvsuppSize));
1073                   PetscCall(DMPlexGetSupport(dm,hv,&hvsupp));
1074                   for (i=0;i<hvsuppSize;i++) {
1075                     PetscInt ep;
1076                     PetscCall(DMPlexGetTreeParent(dm,hvsupp[i],&ep,NULL));
1077                     if (ep != hvsupp[i]) {
1078                       he = hvsupp[i];
1079                     } else {
1080                       skip[i] = PETSC_TRUE;
1081                     }
1082                   }
1083                   PetscCheck(he != -1,PETSC_COMM_SELF,PETSC_ERR_SUP,"Vertex %D support size %D: hanging edge not found",hv,hvsuppSize);
1084                   PetscCall(DMPlexGetCone(dm,he,&cone));
1085                   vids[0] = (cone[0] == hv) ? cone[1] : cone[0];
1086                   PetscCall(DMPlexGetSupportSize(dm,he,&hesuppSize));
1087                   PetscCall(DMPlexGetSupport(dm,he,&hesupp));
1088                   for (f=0;f<hesuppSize;f++) {
1089                     PetscInt j;
1090 
1091                     PetscCall(DMPlexGetCone(dm,hesupp[f],&cone));
1092                     PetscCall(DMPlexGetConeSize(dm,hesupp[f],&coneSize));
1093                     for (j=0;j<coneSize;j++) {
1094                       PetscInt k;
1095                       for (k=0;k<hvsuppSize;k++) {
1096                         if (hvsupp[k] == cone[j]) {
1097                           skip[k] = PETSC_TRUE;
1098                           break;
1099                         }
1100                       }
1101                     }
1102                   }
1103                   for (i=0;i<hvsuppSize;i++) {
1104                     if (!skip[i]) {
1105                       PetscCall(DMPlexGetCone(dm,hvsupp[i],&cone));
1106                       vids[1] = (cone[0] == hv) ? cone[1] : cone[0];
1107                     }
1108                   }
1109                   PetscCall(PetscViewerASCIIPrintf(viewer,"%D",hv-vStart));
1110                   for (i=0;i<2;i++) {
1111                     PetscCall(PetscViewerASCIIPrintf(viewer," %D",vids[i]-vStart));
1112                   }
1113                   PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
1114                   vp--;
1115                 }
1116               }
1117               break;
1118             default:
1119               SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Don't know how to deal with support size %D",ssize);
1120           }
1121         }
1122       }
1123       PetscCall(PetscFree(skip));
1124     }
1125     PetscCheck(!vp,PETSC_COMM_SELF,PETSC_ERR_PLIB,"Unexpected %D hanging vertices",vp);
1126   }
1127   PetscCall(PetscBTDestroy(&vown));
1128 
1129   /* vertices */
1130   if (hovec) { /* higher-order meshes */
1131     const char *fec;
1132     PetscInt   i,n,s;
1133     PetscCall(PetscViewerASCIIPrintf(viewer,"\nvertices\n"));
1134     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",vEnd-vStart));
1135     PetscCall(PetscViewerASCIIPrintf(viewer,"nodes\n"));
1136     PetscCall(PetscObjectGetName((PetscObject)hovec,&fec));
1137     PetscCall(PetscViewerASCIIPrintf(viewer,"FiniteElementSpace\n"));
1138     PetscCall(PetscViewerASCIIPrintf(viewer,"%s\n",fec));
1139     PetscCall(PetscViewerASCIIPrintf(viewer,"VDim: %D\n",sdim));
1140     PetscCall(PetscViewerASCIIPrintf(viewer,"Ordering: 1\n\n")); /*Ordering::byVDIM*/
1141     if (hoSection) {
1142       DM cdm;
1143 
1144       PetscCall(VecGetDM(hovec,&cdm));
1145       for (p=cStart;p<cEnd;p++) {
1146         PetscScalar *vals = NULL;
1147         PetscInt    csize;
1148 
1149         if (PetscUnlikely(pown && !PetscBTLookup(pown,p-cStart))) continue;
1150         PetscCall(DMPlexVecGetClosure(cdm,hoSection,hovec,p,&csize,&vals));
1151         PetscCheck(csize%sdim == 0,PETSC_COMM_SELF,PETSC_ERR_USER,"Size of closure %D incompatible with space dimension %D",csize,sdim);
1152         for (i=0;i<csize/sdim;i++) {
1153           for (s=0;s<sdim;s++) {
1154             PetscCall(PetscViewerASCIIPrintf(viewer,fmt,(double) PetscRealPart(vals[i*sdim+s])));
1155           }
1156           PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
1157         }
1158         PetscCall(DMPlexVecRestoreClosure(cdm,hoSection,hovec,p,&csize,&vals));
1159       }
1160     } else {
1161       PetscCall(VecGetArrayRead(hovec,&array));
1162       PetscCall(VecGetLocalSize(hovec,&n));
1163       PetscCheck(n%sdim == 0,PETSC_COMM_SELF,PETSC_ERR_USER,"Size of local coordinate vector %D incompatible with space dimension %D",n,sdim);
1164       for (i=0;i<n/sdim;i++) {
1165         for (s=0;s<sdim;s++) {
1166           PetscCall(PetscViewerASCIIPrintf(viewer,fmt,(double) PetscRealPart(array[i*sdim+s])));
1167         }
1168         PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
1169       }
1170       PetscCall(VecRestoreArrayRead(hovec,&array));
1171     }
1172   } else {
1173     PetscCall(VecGetLocalSize(coordinates,&nvert));
1174     PetscCall(PetscViewerASCIIPrintf(viewer,"\nvertices\n"));
1175     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",nvert/sdim));
1176     PetscCall(PetscViewerASCIIPrintf(viewer,"%D\n",sdim));
1177     PetscCall(VecGetArrayRead(coordinates,&array));
1178     for (p=0;p<nvert/sdim;p++) {
1179       PetscInt s;
1180       for (s=0;s<sdim;s++) {
1181         PetscReal v = PetscRealPart(array[p*sdim+s]);
1182 
1183         PetscCall(PetscViewerASCIIPrintf(viewer,fmt,PetscIsInfOrNanReal(v) ? 0.0 : (double) v));
1184       }
1185       PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
1186     }
1187     PetscCall(VecRestoreArrayRead(coordinates,&array));
1188   }
1189   PetscCall(PetscBTDestroy(&pown));
1190   PetscCall(VecDestroy(&hovec));
1191   PetscFunctionReturn(0);
1192 }
1193 
1194 PetscErrorCode DMPlexView_GLVis(DM dm, PetscViewer viewer)
1195 {
1196   PetscFunctionBegin;
1197   PetscCall(DMView_GLVis(dm,viewer,DMPlexView_GLVis_ASCII));
1198   PetscFunctionReturn(0);
1199 }
1200