xref: /petsc/src/dm/impls/da/grglvis.c (revision ad540459ab38c4a232710a68077e487eb6627fe2)
1 /* Routines to visualize DMDAs and fields through GLVis */
2 
3 #include <petsc/private/dmdaimpl.h>
4 #include <petsc/private/glvisviewerimpl.h>
5 
6 typedef struct {
7   PetscBool ll;
8 } DMDAGhostedGLVisViewerCtx;
9 
10 static PetscErrorCode DMDAGhostedDestroyGLVisViewerCtx_Private(void **vctx) {
11   PetscFunctionBegin;
12   PetscCall(PetscFree(*vctx));
13   PetscFunctionReturn(0);
14 }
15 
16 typedef struct {
17   Vec xlocal;
18 } DMDAFieldGLVisViewerCtx;
19 
20 static PetscErrorCode DMDAFieldDestroyGLVisViewerCtx_Private(void *vctx) {
21   DMDAFieldGLVisViewerCtx *ctx = (DMDAFieldGLVisViewerCtx *)vctx;
22 
23   PetscFunctionBegin;
24   PetscCall(VecDestroy(&ctx->xlocal));
25   PetscCall(PetscFree(vctx));
26   PetscFunctionReturn(0);
27 }
28 
29 /*
30    dactx->ll is false -> all but the last proc per dimension claim the ghosted node on the right
31    dactx->ll is true -> all but the first proc per dimension claim the ghosted node on the left
32 */
33 static PetscErrorCode DMDAGetNumElementsGhosted(DM da, PetscInt *nex, PetscInt *ney, PetscInt *nez) {
34   DMDAGhostedGLVisViewerCtx *dactx;
35   PetscInt                   sx, sy, sz, ien, jen, ken;
36 
37   PetscFunctionBegin;
38   /* Appease -Wmaybe-uninitialized */
39   if (nex) *nex = -1;
40   if (ney) *ney = -1;
41   if (nez) *nez = -1;
42   PetscCall(DMDAGetCorners(da, &sx, &sy, &sz, &ien, &jen, &ken));
43   PetscCall(DMGetApplicationContext(da, &dactx));
44   if (dactx->ll) {
45     PetscInt dim;
46 
47     PetscCall(DMGetDimension(da, &dim));
48     if (!sx) ien--;
49     if (!sy && dim > 1) jen--;
50     if (!sz && dim > 2) ken--;
51   } else {
52     PetscInt M, N, P;
53 
54     PetscCall(DMDAGetInfo(da, NULL, &M, &N, &P, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
55     if (sx + ien == M) ien--;
56     if (sy + jen == N) jen--;
57     if (sz + ken == P) ken--;
58   }
59   if (nex) *nex = ien;
60   if (ney) *ney = jen;
61   if (nez) *nez = ken;
62   PetscFunctionReturn(0);
63 }
64 
65 /* inherits number of vertices from DMDAGetNumElementsGhosted */
66 static PetscErrorCode DMDAGetNumVerticesGhosted(DM da, PetscInt *nvx, PetscInt *nvy, PetscInt *nvz) {
67   PetscInt ien = 0, jen = 0, ken = 0, dim;
68   PetscInt tote;
69 
70   PetscFunctionBegin;
71   PetscCall(DMGetDimension(da, &dim));
72   PetscCall(DMDAGetNumElementsGhosted(da, &ien, &jen, &ken));
73   tote = ien * (dim > 1 ? jen : 1) * (dim > 2 ? ken : 1);
74   if (tote) {
75     ien = ien + 1;
76     jen = dim > 1 ? jen + 1 : 1;
77     ken = dim > 2 ? ken + 1 : 1;
78   }
79   if (nvx) *nvx = ien;
80   if (nvy) *nvy = jen;
81   if (nvz) *nvz = ken;
82   PetscFunctionReturn(0);
83 }
84 
85 static PetscErrorCode DMDASampleGLVisFields_Private(PetscObject oX, PetscInt nf, PetscObject oXf[], void *vctx) {
86   DM                         da;
87   DMDAFieldGLVisViewerCtx   *ctx = (DMDAFieldGLVisViewerCtx *)vctx;
88   DMDAGhostedGLVisViewerCtx *dactx;
89   const PetscScalar         *array;
90   PetscScalar              **arrayf;
91   PetscInt                   i, f, ii, ien, jen, ken, ie, je, ke, bs, *bss;
92   PetscInt                   sx, sy, sz, gsx, gsy, gsz, ist, jst, kst, gm, gn, gp;
93 
94   PetscFunctionBegin;
95   PetscCall(VecGetDM(ctx->xlocal, &da));
96   PetscCheck(da, PetscObjectComm(oX), PETSC_ERR_ARG_WRONG, "Vector not generated from a DMDA");
97   PetscCall(DMGetApplicationContext(da, &dactx));
98   PetscCall(VecGetBlockSize(ctx->xlocal, &bs));
99   PetscCall(DMGlobalToLocalBegin(da, (Vec)oX, INSERT_VALUES, ctx->xlocal));
100   PetscCall(DMGlobalToLocalEnd(da, (Vec)oX, INSERT_VALUES, ctx->xlocal));
101   PetscCall(DMDAGetNumVerticesGhosted(da, &ien, &jen, &ken));
102   PetscCall(DMDAGetGhostCorners(da, &gsx, &gsy, &gsz, &gm, &gn, &gp));
103   PetscCall(DMDAGetCorners(da, &sx, &sy, &sz, NULL, NULL, NULL));
104   if (dactx->ll) {
105     kst = jst = ist = 0;
106   } else {
107     kst = gsz != sz ? 1 : 0;
108     jst = gsy != sy ? 1 : 0;
109     ist = gsx != sx ? 1 : 0;
110   }
111   PetscCall(PetscMalloc2(nf, &arrayf, nf, &bss));
112   PetscCall(VecGetArrayRead(ctx->xlocal, &array));
113   for (f = 0; f < nf; f++) {
114     PetscCall(VecGetBlockSize((Vec)oXf[f], &bss[f]));
115     PetscCall(VecGetArray((Vec)oXf[f], &arrayf[f]));
116   }
117   for (ke = kst, ii = 0; ke < kst + ken; ke++) {
118     for (je = jst; je < jst + jen; je++) {
119       for (ie = ist; ie < ist + ien; ie++) {
120         PetscInt cf, b;
121         i = ke * gm * gn + je * gm + ie;
122         for (f = 0, cf = 0; f < nf; f++)
123           for (b = 0; b < bss[f]; b++) arrayf[f][bss[f] * ii + b] = array[i * bs + cf++];
124         ii++;
125       }
126     }
127   }
128   for (f = 0; f < nf; f++) PetscCall(VecRestoreArray((Vec)oXf[f], &arrayf[f]));
129   PetscCall(VecRestoreArrayRead(ctx->xlocal, &array));
130   PetscCall(PetscFree2(arrayf, bss));
131   PetscFunctionReturn(0);
132 }
133 
134 PETSC_INTERN PetscErrorCode DMSetUpGLVisViewer_DMDA(PetscObject oda, PetscViewer viewer) {
135   DM da = (DM)oda, daview;
136 
137   PetscFunctionBegin;
138   PetscCall(PetscObjectQuery(oda, "GLVisGraphicsDMDAGhosted", (PetscObject *)&daview));
139   if (!daview) {
140     DMDAGhostedGLVisViewerCtx *dactx;
141     DM                         dacoord = NULL;
142     Vec                        xcoor, xcoorl;
143     PetscBool                  hashocoord = PETSC_FALSE;
144     const PetscInt            *lx, *ly, *lz;
145     PetscInt                   dim, M, N, P, m, n, p, dof, s, i;
146 
147     PetscCall(PetscNew(&dactx));
148     dactx->ll = PETSC_TRUE; /* default to match elements layout obtained by DMDAGetElements */
149     PetscOptionsBegin(PetscObjectComm(oda), oda->prefix, "GLVis PetscViewer DMDA Options", "PetscViewer");
150     PetscCall(PetscOptionsBool("-viewer_glvis_dm_da_ll", "Left-looking subdomain view", NULL, dactx->ll, &dactx->ll, NULL));
151     PetscOptionsEnd();
152     /* Create a properly ghosted DMDA to visualize the mesh and the fields associated with */
153     PetscCall(DMDAGetInfo(da, &dim, &M, &N, &P, &m, &n, &p, &dof, &s, NULL, NULL, NULL, NULL));
154     PetscCall(DMDAGetOwnershipRanges(da, &lx, &ly, &lz));
155     PetscCall(PetscObjectQuery((PetscObject)da, "_glvis_mesh_coords", (PetscObject *)&xcoor));
156     if (!xcoor) {
157       PetscCall(DMGetCoordinates(da, &xcoor));
158     } else {
159       hashocoord = PETSC_TRUE;
160     }
161     PetscCall(PetscInfo(da, "Creating auxiliary DMDA for managing GLVis graphics\n"));
162     switch (dim) {
163     case 1:
164       PetscCall(DMDACreate1d(PetscObjectComm((PetscObject)da), DM_BOUNDARY_NONE, M, dof, 1, lx, &daview));
165       if (!hashocoord) PetscCall(DMDACreate1d(PetscObjectComm((PetscObject)da), DM_BOUNDARY_NONE, M, 1, 1, lx, &dacoord));
166       break;
167     case 2:
168       PetscCall(DMDACreate2d(PetscObjectComm((PetscObject)da), DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, M, N, m, n, dof, 1, lx, ly, &daview));
169       if (!hashocoord) PetscCall(DMDACreate2d(PetscObjectComm((PetscObject)da), DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, M, N, m, n, 2, 1, lx, ly, &dacoord));
170       break;
171     case 3:
172       PetscCall(DMDACreate3d(PetscObjectComm((PetscObject)da), DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, M, N, P, m, n, p, dof, 1, lx, ly, lz, &daview));
173       if (!hashocoord) PetscCall(DMDACreate3d(PetscObjectComm((PetscObject)da), DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, M, N, P, m, n, p, 3, 1, lx, ly, lz, &dacoord));
174       break;
175     default: SETERRQ(PetscObjectComm((PetscObject)da), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT, dim);
176     }
177     PetscCall(DMSetApplicationContext(daview, dactx));
178     PetscCall(DMSetApplicationContextDestroy(daview, DMDAGhostedDestroyGLVisViewerCtx_Private));
179     PetscCall(DMSetUp(daview));
180     if (!xcoor) {
181       PetscCall(DMDASetUniformCoordinates(daview, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
182       PetscCall(DMGetCoordinates(daview, &xcoor));
183     }
184     if (dacoord) {
185       PetscCall(DMSetUp(dacoord));
186       PetscCall(DMCreateLocalVector(dacoord, &xcoorl));
187       PetscCall(DMGlobalToLocalBegin(dacoord, xcoor, INSERT_VALUES, xcoorl));
188       PetscCall(DMGlobalToLocalEnd(dacoord, xcoor, INSERT_VALUES, xcoorl));
189       PetscCall(DMDestroy(&dacoord));
190     } else {
191       PetscInt    ien, jen, ken, nc, nl, cdof, deg;
192       char        fecmesh[64];
193       const char *name;
194       PetscBool   flg;
195 
196       PetscCall(DMDAGetNumElementsGhosted(daview, &ien, &jen, &ken));
197       nc = ien * (jen > 0 ? jen : 1) * (ken > 0 ? ken : 1);
198 
199       PetscCall(VecGetLocalSize(xcoor, &nl));
200       PetscCheck(!nc || (nl % nc) == 0, PETSC_COMM_SELF, PETSC_ERR_SUP, "Incompatible local coordinate size %" PetscInt_FMT " and number of cells %" PetscInt_FMT, nl, nc);
201       PetscCall(VecDuplicate(xcoor, &xcoorl));
202       PetscCall(VecCopy(xcoor, xcoorl));
203       PetscCall(VecSetDM(xcoorl, NULL));
204       PetscCall(PetscObjectGetName((PetscObject)xcoor, &name));
205       PetscCall(PetscStrbeginswith(name, "FiniteElementCollection:", &flg));
206       if (!flg) {
207         deg = 0;
208         if (nc && nl) {
209           cdof = nl / (nc * dim);
210           deg  = 1;
211           while (1) {
212             PetscInt degd = 1;
213             for (i = 0; i < dim; i++) degd *= (deg + 1);
214             PetscCheck(degd <= cdof, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cell dofs %" PetscInt_FMT, cdof);
215             if (degd == cdof) break;
216             deg++;
217           }
218         }
219         PetscCall(PetscSNPrintf(fecmesh, sizeof(fecmesh), "FiniteElementCollection: L2_T1_%" PetscInt_FMT "D_P%" PetscInt_FMT, dim, deg));
220         PetscCall(PetscObjectSetName((PetscObject)xcoorl, fecmesh));
221       } else PetscCall(PetscObjectSetName((PetscObject)xcoorl, name));
222     }
223 
224     /* xcoorl is composed with the ghosted DMDA, the ghosted coordinate DMDA (if present) is only available through this vector */
225     PetscCall(PetscObjectCompose((PetscObject)daview, "GLVisGraphicsCoordsGhosted", (PetscObject)xcoorl));
226     PetscCall(PetscObjectDereference((PetscObject)xcoorl));
227 
228     /* daview is composed with the original DMDA */
229     PetscCall(PetscObjectCompose(oda, "GLVisGraphicsDMDAGhosted", (PetscObject)daview));
230     PetscCall(PetscObjectDereference((PetscObject)daview));
231   }
232 
233   /* customize the viewer if present */
234   if (viewer) {
235     DMDAFieldGLVisViewerCtx   *ctx;
236     DMDAGhostedGLVisViewerCtx *dactx;
237     char                       fec[64];
238     Vec                        xlocal, *Ufield;
239     const char               **dafieldname;
240     char                     **fec_type, **fieldname;
241     PetscInt                  *nlocal, *bss, *dims;
242     PetscInt                   dim, M, N, P, dof, s, i, nf;
243     PetscBool                  bsset;
244 
245     PetscCall(DMDAGetInfo(daview, &dim, NULL, NULL, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL));
246     PetscCall(DMGetApplicationContext(daview, &dactx));
247     PetscCall(DMCreateLocalVector(daview, &xlocal));
248     PetscCall(DMDAGetFieldNames(da, (const char *const **)&dafieldname));
249     PetscCall(DMDAGetNumVerticesGhosted(daview, &M, &N, &P));
250     PetscCall(PetscSNPrintf(fec, sizeof(fec), "FiniteElementCollection: H1_%" PetscInt_FMT "D_P1", dim));
251     PetscCall(PetscMalloc6(dof, &fec_type, dof, &nlocal, dof, &bss, dof, &dims, dof, &fieldname, dof, &Ufield));
252     for (i = 0; i < dof; i++) bss[i] = 1;
253     nf = dof;
254 
255     PetscOptionsBegin(PetscObjectComm(oda), oda->prefix, "GLVis PetscViewer DMDA Field options", "PetscViewer");
256     PetscCall(PetscOptionsIntArray("-viewer_glvis_dm_da_bs", "Block sizes for subfields; enable vector representation", NULL, bss, &nf, &bsset));
257     PetscOptionsEnd();
258     if (bsset) {
259       PetscInt t;
260       for (i = 0, t = 0; i < nf; i++) t += bss[i];
261       PetscCheck(t == dof, PetscObjectComm(oda), PETSC_ERR_USER, "Sum of block sizes %" PetscInt_FMT " should equal %" PetscInt_FMT, t, dof);
262     } else nf = dof;
263 
264     for (i = 0, s = 0; i < nf; i++) {
265       PetscCall(PetscStrallocpy(fec, &fec_type[i]));
266       if (bss[i] == 1) {
267         PetscCall(PetscStrallocpy(dafieldname[s], &fieldname[i]));
268       } else {
269         PetscInt b;
270         size_t   tlen = 9; /* "Vector-" + end */
271         for (b = 0; b < bss[i]; b++) {
272           size_t len;
273           PetscCall(PetscStrlen(dafieldname[s + b], &len));
274           tlen += len + 1; /* field + "-" */
275         }
276         PetscCall(PetscMalloc1(tlen, &fieldname[i]));
277         PetscCall(PetscStrcpy(fieldname[i], "Vector-"));
278         for (b = 0; b < bss[i] - 1; b++) {
279           PetscCall(PetscStrcat(fieldname[i], dafieldname[s + b]));
280           PetscCall(PetscStrcat(fieldname[i], "-"));
281         }
282         PetscCall(PetscStrcat(fieldname[i], dafieldname[s + b]));
283       }
284       dims[i]   = dim;
285       nlocal[i] = M * N * P * bss[i];
286       s += bss[i];
287     }
288 
289     /* the viewer context takes ownership of xlocal and destroys it in DMDAFieldDestroyGLVisViewerCtx_Private */
290     PetscCall(PetscNew(&ctx));
291     ctx->xlocal = xlocal;
292 
293     /* create work vectors */
294     for (i = 0; i < nf; i++) {
295       PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)da), nlocal[i], PETSC_DECIDE, &Ufield[i]));
296       PetscCall(PetscObjectSetName((PetscObject)Ufield[i], fieldname[i]));
297       PetscCall(VecSetBlockSize(Ufield[i], bss[i]));
298       PetscCall(VecSetDM(Ufield[i], da));
299     }
300 
301     PetscCall(PetscViewerGLVisSetFields(viewer, nf, (const char **)fec_type, dims, DMDASampleGLVisFields_Private, (PetscObject *)Ufield, ctx, DMDAFieldDestroyGLVisViewerCtx_Private));
302     for (i = 0; i < nf; i++) {
303       PetscCall(PetscFree(fec_type[i]));
304       PetscCall(PetscFree(fieldname[i]));
305       PetscCall(VecDestroy(&Ufield[i]));
306     }
307     PetscCall(PetscFree6(fec_type, nlocal, bss, dims, fieldname, Ufield));
308   }
309   PetscFunctionReturn(0);
310 }
311 
312 static PetscErrorCode DMDAView_GLVis_ASCII(DM dm, PetscViewer viewer) {
313   DM                 da, cda;
314   Vec                xcoorl;
315   PetscMPIInt        size;
316   const PetscScalar *array;
317   PetscContainer     glvis_container;
318   PetscInt           dim, sdim, i, vid[8], mid, cid, cdof;
319   PetscInt           sx, sy, sz, ie, je, ke, ien, jen, ken, nel;
320   PetscInt           gsx, gsy, gsz, gm, gn, gp, kst, jst, ist;
321   PetscBool          enabled = PETSC_TRUE, isascii;
322   const char        *fmt;
323 
324   PetscFunctionBegin;
325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
326   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
327   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
328   PetscCheck(isascii, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Viewer must be of type VIEWERASCII");
329   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)viewer), &size));
330   PetscCheck(size <= 1, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Use single sequential viewers for parallel visualization");
331   PetscCall(DMGetDimension(dm, &dim));
332 
333   /* get container: determines if a process visualizes is portion of the data or not */
334   PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&glvis_container));
335   PetscCheck(glvis_container, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing GLVis container");
336   {
337     PetscViewerGLVisInfo glvis_info;
338     PetscCall(PetscContainerGetPointer(glvis_container, (void **)&glvis_info));
339     enabled = glvis_info->enabled;
340     fmt     = glvis_info->fmt;
341   }
342   /* this can happen if we are calling DMView outside of VecView_GLVis */
343   PetscCall(PetscObjectQuery((PetscObject)dm, "GLVisGraphicsDMDAGhosted", (PetscObject *)&da));
344   if (!da) PetscCall(DMSetUpGLVisViewer_DMDA((PetscObject)dm, NULL));
345   PetscCall(PetscObjectQuery((PetscObject)dm, "GLVisGraphicsDMDAGhosted", (PetscObject *)&da));
346   PetscCheck(da, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing GLVis ghosted DMDA");
347   PetscCall(DMGetCoordinateDim(da, &sdim));
348 
349   PetscCall(PetscViewerASCIIPrintf(viewer, "MFEM mesh v1.0\n"));
350   PetscCall(PetscViewerASCIIPrintf(viewer, "\ndimension\n"));
351   PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", dim));
352 
353   if (!enabled) {
354     PetscCall(PetscViewerASCIIPrintf(viewer, "\nelements\n"));
355     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
356     PetscCall(PetscViewerASCIIPrintf(viewer, "\nboundary\n"));
357     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
358     PetscCall(PetscViewerASCIIPrintf(viewer, "\nvertices\n"));
359     PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
360     PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", sdim));
361     PetscFunctionReturn(0);
362   }
363 
364   PetscCall(DMDAGetNumElementsGhosted(da, &ien, &jen, &ken));
365   nel = ien;
366   if (dim > 1) nel *= jen;
367   if (dim > 2) nel *= ken;
368   PetscCall(PetscViewerASCIIPrintf(viewer, "\nelements\n"));
369   PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", nel));
370   switch (dim) {
371   case 1:
372     for (ie = 0; ie < ien; ie++) {
373       vid[0] = ie;
374       vid[1] = ie + 1;
375       mid    = 1; /* material id */
376       cid    = 1; /* segment */
377       PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT "\n", mid, cid, vid[0], vid[1]));
378     }
379     break;
380   case 2:
381     for (je = 0; je < jen; je++) {
382       for (ie = 0; ie < ien; ie++) {
383         vid[0] = je * (ien + 1) + ie;
384         vid[1] = je * (ien + 1) + ie + 1;
385         vid[2] = (je + 1) * (ien + 1) + ie + 1;
386         vid[3] = (je + 1) * (ien + 1) + ie;
387         mid    = 1; /* material id */
388         cid    = 3; /* quad */
389         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT "\n", mid, cid, vid[0], vid[1], vid[2], vid[3]));
390       }
391     }
392     break;
393   case 3:
394     for (ke = 0; ke < ken; ke++) {
395       for (je = 0; je < jen; je++) {
396         for (ie = 0; ie < ien; ie++) {
397           vid[0] = ke * (jen + 1) * (ien + 1) + je * (ien + 1) + ie;
398           vid[1] = ke * (jen + 1) * (ien + 1) + je * (ien + 1) + ie + 1;
399           vid[2] = ke * (jen + 1) * (ien + 1) + (je + 1) * (ien + 1) + ie + 1;
400           vid[3] = ke * (jen + 1) * (ien + 1) + (je + 1) * (ien + 1) + ie;
401           vid[4] = (ke + 1) * (jen + 1) * (ien + 1) + je * (ien + 1) + ie;
402           vid[5] = (ke + 1) * (jen + 1) * (ien + 1) + je * (ien + 1) + ie + 1;
403           vid[6] = (ke + 1) * (jen + 1) * (ien + 1) + (je + 1) * (ien + 1) + ie + 1;
404           vid[7] = (ke + 1) * (jen + 1) * (ien + 1) + (je + 1) * (ien + 1) + ie;
405           mid    = 1; /* material id */
406           cid    = 5; /* hex */
407           PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT "\n", mid, cid, vid[0], vid[1], vid[2], vid[3], vid[4], vid[5], vid[6], vid[7]));
408         }
409       }
410     }
411     break;
412   default: SETERRQ(PetscObjectComm((PetscObject)da), PETSC_ERR_SUP, "Unsupported dimension %" PetscInt_FMT, dim);
413   }
414   PetscCall(PetscViewerASCIIPrintf(viewer, "\nboundary\n"));
415   PetscCall(PetscViewerASCIIPrintf(viewer, "0\n"));
416 
417   /* vertex coordinates */
418   PetscCall(PetscObjectQuery((PetscObject)da, "GLVisGraphicsCoordsGhosted", (PetscObject *)&xcoorl));
419   PetscCheck(xcoorl, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing GLVis ghosted coords");
420   PetscCall(DMDAGetNumVerticesGhosted(da, &ien, &jen, &ken));
421   PetscCall(PetscViewerASCIIPrintf(viewer, "\nvertices\n"));
422   PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", ien * jen * ken));
423   if (nel) {
424     PetscCall(VecGetDM(xcoorl, &cda));
425     PetscCall(VecGetArrayRead(xcoorl, &array));
426     if (!cda) { /* HO viz */
427       const char *fecname;
428       PetscInt    nc, nl;
429 
430       PetscCall(PetscObjectGetName((PetscObject)xcoorl, &fecname));
431       PetscCall(PetscViewerASCIIPrintf(viewer, "nodes\n"));
432       PetscCall(PetscViewerASCIIPrintf(viewer, "FiniteElementSpace\n"));
433       PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", fecname));
434       PetscCall(PetscViewerASCIIPrintf(viewer, "VDim: %" PetscInt_FMT "\n", sdim));
435       PetscCall(PetscViewerASCIIPrintf(viewer, "Ordering: 1\n\n")); /*Ordering::byVDIM*/
436       /* L2 coordinates */
437       PetscCall(DMDAGetNumElementsGhosted(da, &ien, &jen, &ken));
438       PetscCall(VecGetLocalSize(xcoorl, &nl));
439       nc   = ien * (jen > 0 ? jen : 1) * (ken > 0 ? ken : 1);
440       cdof = nc ? nl / nc : 0;
441       if (!ien) ien++;
442       if (!jen) jen++;
443       if (!ken) ken++;
444       ist = jst = kst = 0;
445       gm              = ien;
446       gn              = jen;
447       gp              = ken;
448     } else {
449       DMDAGhostedGLVisViewerCtx *dactx;
450 
451       PetscCall(DMGetApplicationContext(da, &dactx));
452       PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "\n", sdim));
453       cdof = sdim;
454       PetscCall(DMDAGetCorners(da, &sx, &sy, &sz, NULL, NULL, NULL));
455       PetscCall(DMDAGetGhostCorners(da, &gsx, &gsy, &gsz, &gm, &gn, &gp));
456       if (dactx->ll) {
457         kst = jst = ist = 0;
458       } else {
459         kst = gsz != sz ? 1 : 0;
460         jst = gsy != sy ? 1 : 0;
461         ist = gsx != sx ? 1 : 0;
462       }
463     }
464     for (ke = kst; ke < kst + ken; ke++) {
465       for (je = jst; je < jst + jen; je++) {
466         for (ie = ist; ie < ist + ien; ie++) {
467           PetscInt c;
468 
469           i = ke * gm * gn + je * gm + ie;
470           for (c = 0; c < cdof / sdim; c++) {
471             PetscInt d;
472             for (d = 0; d < sdim; d++) PetscCall(PetscViewerASCIIPrintf(viewer, fmt, PetscRealPart(array[cdof * i + c * sdim + d])));
473             PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
474           }
475         }
476       }
477     }
478     PetscCall(VecRestoreArrayRead(xcoorl, &array));
479   }
480   PetscFunctionReturn(0);
481 }
482 
483 PetscErrorCode DMView_DA_GLVis(DM dm, PetscViewer viewer) {
484   PetscFunctionBegin;
485   PetscCall(DMView_GLVis(dm, viewer, DMDAView_GLVis_ASCII));
486   PetscFunctionReturn(0);
487 }
488