Actual source code: glvis.c

  1: #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fdopen() */

  3: #include <petsc/private/viewerimpl.h>
  4: #include <petsc/private/petscimpl.h>
  5: #include <petsc/private/glvisviewerimpl.h>

  7: /* we may eventually make this function public */
  8: static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm, const char *, PetscInt, PetscViewer *);

 10: struct _n_PetscViewerGLVis {
 11:   PetscViewerGLVisStatus status;
 12:   PetscViewerGLVisType   type;       /* either PETSC_VIEWER_GLVIS_DUMP or PETSC_VIEWER_GLVIS_SOCKET */
 13:   char                  *name;       /* prefix for filename, or hostname, depending on the type */
 14:   PetscInt               port;       /* used just for the socket case */
 15:   PetscReal              pause;      /* if positive, calls PetscSleep(pause) after each VecView_GLVis call */
 16:   PetscViewer            meshwindow; /* used just by the ASCII dumping */
 17:   PetscObject            dm;         /* DM as passed by PetscViewerGLVisSetDM_Private(): should contain discretization info */
 18:   PetscInt               nwindow;    /* number of windows/fields to be visualized */
 19:   PetscViewer           *window;
 20:   char                 **windowtitle;
 21:   PetscInt               windowsizes[2];
 22:   char                 **fec_type;                                          /* type of elements to be used for visualization, see FiniteElementCollection::Name() */
 23:   PetscErrorCode (*g2lfield)(PetscObject, PetscInt, PetscObject[], void *); /* global to local operation for generating dofs to be visualized */
 24:   PetscInt    *spacedim;                                                    /* geometrical space dimension (just used to initialize the scene) */
 25:   PetscObject *Ufield;                                                      /* work vectors for visualization */
 26:   PetscInt     snapid;                                                      /* snapshot id, use PetscViewerGLVisSetSnapId to change this value*/
 27:   void        *userctx;                                                     /* User context, used by g2lfield */
 28:   PetscErrorCode (*destroyctx)(void *);                                     /* destroy routine for userctx */
 29:   char *fmt;                                                                /* format string for FP values */
 30: };
 31: typedef struct _n_PetscViewerGLVis *PetscViewerGLVis;

 33: /*@
 34:   PetscViewerGLVisSetPrecision - Set the number of digits for floating point values to be displayed

 36:   Not Collective

 38:   Input Parameters:
 39: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
 40: - prec   - the number of digits required

 42:   Level: beginner

 44: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
 45: @*/
 46: PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec)
 47: {
 48:   PetscFunctionBegin;
 50:   PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec));
 51:   PetscFunctionReturn(PETSC_SUCCESS);
 52: }

 54: static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec)
 55: {
 56:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

 58:   PetscFunctionBegin;
 59:   PetscCall(PetscFree(socket->fmt));
 60:   if (prec > 0) {
 61:     PetscCall(PetscMalloc1(16, &socket->fmt));
 62:     PetscCall(PetscSNPrintf(socket->fmt, 16, " %%.%" PetscInt_FMT "e", prec));
 63:   } else {
 64:     PetscCall(PetscStrallocpy(" %g", &socket->fmt));
 65:   }
 66:   PetscFunctionReturn(PETSC_SUCCESS);
 67: }

 69: /*@
 70:   PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP`

 72:   Logically Collective

 74:   Input Parameters:
 75: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
 76: - id     - the current snapshot id in a time-dependent simulation

 78:   Level: beginner

 80: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
 81: @*/
 82: PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id)
 83: {
 84:   PetscFunctionBegin;
 87:   PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id));
 88:   PetscFunctionReturn(PETSC_SUCCESS);
 89: }

 91: static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id)
 92: {
 93:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

 95:   PetscFunctionBegin;
 96:   socket->snapid = id;
 97:   PetscFunctionReturn(PETSC_SUCCESS);
 98: }

100: /*@C
101:   PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector.

103:   Logically Collective

105:   Input Parameters:
106: + viewer     - the `PetscViewer` of type `PETSCVIEWERGLVIS`
107: . nf         - number of fields to be visualized
108: . fec_type   - the type of finite element to be used to visualize the data (see FiniteElementCollection::Name() in MFEM)
109: . dim        - array of space dimension for field vectors (used to initialize the scene)
110: . g2l        - User routine to compute the local field vectors to be visualized; PetscObject is used in place of Vec on the prototype
111: . Vfield     - array of work vectors, one for each field
112: . ctx        - User context to store the relevant data to apply g2lfields
113: - destroyctx - Destroy function for userctx

115:   Level: intermediate

117:   Notes:
118:   `g2lfields` is called on the vector V to be visualized in order to extract the relevant dofs to be put in `Vfield`, as
119: .vb
120:   g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx).
121: .ve

123:   For vector spaces, the block size of `Vfield`[i] represents the vector dimension.
124:   The names of the `Vfield` vectors will be displayed in the window title.

126: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscObjectSetName()`
127: @*/
128: PetscErrorCode PetscViewerGLVisSetFields(PetscViewer viewer, PetscInt nf, const char *fec_type[], PetscInt dim[], PetscErrorCode (*g2l)(PetscObject, PetscInt, PetscObject[], void *), PetscObject Vfield[], void *ctx, PetscErrorCode (*destroyctx)(void *))
129: {
130:   PetscFunctionBegin;
133:   PetscCheck(fec_type, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "You need to provide the FiniteElementCollection names for the fields");
134:   PetscAssertPointer(fec_type, 3);
135:   PetscAssertPointer(dim, 4);
136:   PetscAssertPointer(Vfield, 6);
137:   PetscTryMethod(viewer, "PetscViewerGLVisSetFields_C", (PetscViewer, PetscInt, const char *[], PetscInt[], PetscErrorCode (*)(PetscObject, PetscInt, PetscObject[], void *), PetscObject[], void *, PetscErrorCode (*)(void *)), (viewer, nf, fec_type, dim, g2l, Vfield, ctx, destroyctx));
138:   PetscFunctionReturn(PETSC_SUCCESS);
139: }

141: static PetscErrorCode PetscViewerGLVisSetFields_GLVis(PetscViewer viewer, PetscInt nfields, const char *fec_type[], PetscInt dim[], PetscErrorCode (*g2l)(PetscObject, PetscInt, PetscObject[], void *), PetscObject Vfield[], void *ctx, PetscErrorCode (*destroyctx)(void *))
142: {
143:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
144:   PetscInt         i;

146:   PetscFunctionBegin;
147:   PetscCheck(!socket->nwindow || socket->nwindow == nfields, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Cannot set number of fields %" PetscInt_FMT " with number of windows %" PetscInt_FMT, nfields, socket->nwindow);
148:   if (!socket->nwindow) {
149:     socket->nwindow = nfields;

151:     PetscCall(PetscCalloc5(nfields, &socket->window, nfields, &socket->windowtitle, nfields, &socket->fec_type, nfields, &socket->spacedim, nfields, &socket->Ufield));
152:     for (i = 0; i < nfields; i++) {
153:       const char *name;

155:       PetscCall(PetscObjectGetName(Vfield[i], &name));
156:       PetscCall(PetscStrallocpy(name, &socket->windowtitle[i]));
157:       PetscCall(PetscStrallocpy(fec_type[i], &socket->fec_type[i]));
158:       PetscCall(PetscObjectReference(Vfield[i]));
159:       socket->Ufield[i]   = Vfield[i];
160:       socket->spacedim[i] = dim[i];
161:     }
162:   }
163:   /* number of fields are not allowed to vary */
164:   PetscCheck(nfields == socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot visualize %" PetscInt_FMT " fields using %" PetscInt_FMT " socket windows", nfields, socket->nwindow);
165:   socket->g2lfield = g2l;
166:   if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));
167:   socket->userctx    = ctx;
168:   socket->destroyctx = destroyctx;
169:   PetscFunctionReturn(PETSC_SUCCESS);
170: }

172: static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr)
173: {
174:   PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr;

176:   PetscFunctionBegin;
177:   PetscCall(PetscFree(info->fmt));
178:   PetscCall(PetscFree(info));
179:   PetscFunctionReturn(PETSC_SUCCESS);
180: }

182: /* we can decide to prevent specific processes from using the viewer */
183: static PetscErrorCode PetscViewerGLVisAttachInfo_Private(PetscViewer viewer, PetscViewer window)
184: {
185:   PetscViewerGLVis     socket = (PetscViewerGLVis)viewer->data;
186:   PetscContainer       container;
187:   PetscViewerGLVisInfo info;

189:   PetscFunctionBegin;
190:   PetscCall(PetscObjectQuery((PetscObject)window, "_glvis_info_container", (PetscObject *)&container));
191:   if (!container) {
192:     PetscCall(PetscNew(&info));
193:     info->enabled = PETSC_TRUE;
194:     info->init    = PETSC_FALSE;
195:     info->size[0] = socket->windowsizes[0];
196:     info->size[1] = socket->windowsizes[1];
197:     info->pause   = socket->pause;
198:     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)window), &container));
199:     PetscCall(PetscContainerSetPointer(container, (void *)info));
200:     PetscCall(PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private));
201:     PetscCall(PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container));
202:     PetscCall(PetscContainerDestroy(&container));
203:   } else {
204:     PetscCall(PetscContainerGetPointer(container, (void **)&info));
205:   }
206:   PetscCall(PetscFree(info->fmt));
207:   PetscCall(PetscStrallocpy(socket->fmt, &info->fmt));
208:   PetscFunctionReturn(PETSC_SUCCESS);
209: }

211: static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view)
212: {
213:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
214:   PetscViewer      window = NULL;
215:   PetscBool        ldis, dis;

217:   PetscFunctionBegin;
218:   PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window));
219:   /* if we could not establish a connection, we disable the socket viewer on all MPI ranks */
220:   ldis = !viewer ? PETSC_TRUE : PETSC_FALSE;
221:   PetscCall(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer)));
222:   if (dis) {
223:     socket->status = PETSCVIEWERGLVIS_DISABLED;
224:     PetscCall(PetscViewerDestroy(&window));
225:   }
226:   *view = window;
227:   PetscFunctionReturn(PETSC_SUCCESS);
228: }

230: PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer)
231: {
232:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

234:   PetscFunctionBegin;
235:   if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause));
236:   PetscFunctionReturn(PETSC_SUCCESS);
237: }

239: /* DM specific support */
240: PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm)
241: {
242:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

244:   PetscFunctionBegin;
245:   PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer");
246:   if (!socket->dm) {
247:     PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL;

249:     PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm));
250:     if (setupwithdm) {
251:       PetscCall((*setupwithdm)(dm, viewer));
252:     } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
253:     PetscCall(PetscObjectReference(dm));
254:     socket->dm = dm;
255:   }
256:   PetscFunctionReturn(PETSC_SUCCESS);
257: }

259: PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
260: {
261:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

263:   PetscFunctionBegin;
264:   PetscAssertPointer(view, 2);
265:   if (!socket->meshwindow) {
266:     if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) {
267:       PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow));
268:     } else {
269:       size_t    len;
270:       PetscBool isstdout;

272:       PetscCall(PetscStrlen(socket->name, &len));
273:       PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
274:       if (!socket->name || !len || isstdout) {
275:         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow));
276:       } else {
277:         PetscMPIInt rank;
278:         char        filename[PETSC_MAX_PATH_LEN];
279:         PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
280:         PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank));
281:         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow));
282:       }
283:     }
284:     if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS));
285:   }
286:   if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow));
287:   *view = socket->meshwindow;
288:   PetscFunctionReturn(PETSC_SUCCESS);
289: }

291: PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
292: {
293:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

295:   PetscFunctionBegin;
296:   PetscAssertPointer(view, 2);
297:   PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()");
298:   if (*view) {
299:     PetscCall(PetscViewerFlush(*view));
300:     PetscCall(PetscBarrier((PetscObject)viewer));
301:   }
302:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
303:     PetscCall(PetscViewerDestroy(&socket->meshwindow));
304:   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
305:     socket->meshwindow = NULL;
306:   }
307:   *view = NULL;
308:   PetscFunctionReturn(PETSC_SUCCESS);
309: }

311: PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
312: {
313:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

315:   PetscFunctionBegin;
316:   PetscAssertPointer(type, 2);
317:   *type = socket->type;
318:   PetscFunctionReturn(PETSC_SUCCESS);
319: }

321: /* This function is only relevant in the SOCKET_GLIVS case. The status is computed the first time it is requested, as GLVis currently has issues when connecting the first time through the socket */
322: PetscErrorCode PetscViewerGLVisGetStatus_Internal(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
323: {
324:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

326:   PetscFunctionBegin;
327:   PetscAssertPointer(sockstatus, 2);
328:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
329:     socket->status = PETSCVIEWERGLVIS_DISCONNECTED;
330:   } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) {
331:     PetscInt  i;
332:     PetscBool lconn, conn;

334:     for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
335:       if (!socket->window[i]) lconn = PETSC_FALSE;

337:     PetscCall(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer)));
338:     if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
339:   }
340:   *sockstatus = socket->status;
341:   PetscFunctionReturn(PETSC_SUCCESS);
342: }

344: PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
345: {
346:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

348:   PetscFunctionBegin;
349:   *dm = socket->dm;
350:   PetscFunctionReturn(PETSC_SUCCESS);
351: }

353: PetscErrorCode PetscViewerGLVisGetFields_Internal(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx)
354: {
355:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

357:   PetscFunctionBegin;
358:   if (nfield) *nfield = socket->nwindow;
359:   if (fec) *fec = (const char **)socket->fec_type;
360:   if (spacedim) *spacedim = socket->spacedim;
361:   if (g2lfield) *g2lfield = socket->g2lfield;
362:   if (Ufield) *Ufield = socket->Ufield;
363:   if (userctx) *userctx = socket->userctx;
364:   PetscFunctionReturn(PETSC_SUCCESS);
365: }

367: /* accessor routines for the viewer windows:
368:    PETSC_VIEWER_GLVIS_DUMP   : it returns a new viewer every time
369:    PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done.
370: */
371: PetscErrorCode PetscViewerGLVisGetWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
372: {
373:   PetscViewerGLVis       socket = (PetscViewerGLVis)viewer->data;
374:   PetscViewerGLVisStatus status;

376:   PetscFunctionBegin;
378:   PetscAssertPointer(view, 3);
379:   PetscCheck(wid >= 0 && (wid <= socket->nwindow - 1), PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Cannot get window id %" PetscInt_FMT ": allowed range [0,%" PetscInt_FMT ")", wid, socket->nwindow - 1);
380:   status = socket->status;
381:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "Window %" PetscInt_FMT " is already in use", wid);
382:   switch (status) {
383:   case PETSCVIEWERGLVIS_DISCONNECTED:
384:     PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen");
385:     if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
386:       size_t    len;
387:       PetscBool isstdout;

389:       PetscCall(PetscStrlen(socket->name, &len));
390:       PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
391:       if (!socket->name || !len || isstdout) {
392:         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid]));
393:       } else {
394:         PetscMPIInt rank;
395:         char        filename[PETSC_MAX_PATH_LEN];

397:         PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
398:         PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank));
399:         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid]));
400:       }
401:     } else {
402:       PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid]));
403:     }
404:     if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS));
405:     *view = socket->window[wid];
406:     break;
407:   case PETSCVIEWERGLVIS_CONNECTED:
408:     *view = socket->window[wid];
409:     break;
410:   case PETSCVIEWERGLVIS_DISABLED:
411:     *view = NULL;
412:     break;
413:   default:
414:     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status);
415:   }
416:   if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view));
417:   PetscFunctionReturn(PETSC_SUCCESS);
418: }

420: /* Restore the window viewer
421:    PETSC_VIEWER_GLVIS_DUMP  : destroys the temporary created ASCII viewer used for dumping
422:    PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer.
423:                  - it the returned window viewer is NULL, assumes something went wrong
424:                    with the socket (i.e. SIGPIPE when a user closes the popup window)
425:                    and that the caller already handled it (see VecView_GLVis).
426: */
427: PetscErrorCode PetscViewerGLVisRestoreWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
428: {
429:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

431:   PetscFunctionBegin;
434:   PetscAssertPointer(view, 3);
435:   PetscCheck(wid >= 0 && wid < socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Cannot restore window id %" PetscInt_FMT ": allowed range [0,%" PetscInt_FMT ")", wid, socket->nwindow);
436:   PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()");
437:   if (*view) {
438:     PetscCall(PetscViewerFlush(*view));
439:     PetscCall(PetscBarrier((PetscObject)viewer));
440:   }
441:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
442:     PetscCall(PetscViewerDestroy(&socket->window[wid]));
443:   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
444:     socket->window[wid] = NULL;
445:   }
446:   *view = NULL;
447:   PetscFunctionReturn(PETSC_SUCCESS);
448: }

450: /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
451: PetscErrorCode PetscViewerGLVisInitWindow_Internal(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
452: {
453:   PetscViewerGLVisInfo info;
454:   PetscContainer       container;

456:   PetscFunctionBegin;
457:   PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container));
458:   PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private");
459:   PetscCall(PetscContainerGetPointer(container, (void **)&info));
460:   if (info->init) PetscFunctionReturn(PETSC_SUCCESS);

462:   /* Configure window */
463:   if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1]));
464:   if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name));

466:   /* Configure default view */
467:   if (mesh) {
468:     switch (dim) {
469:     case 1:
470:       PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
471:       break;
472:     case 2:
473:       PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
474:       break;
475:     case 3: /* TODO: decide default view in 3D */
476:       break;
477:     }
478:   } else {
479:     PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */
480:     switch (dim) {
481:     case 1:
482:       PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */
483:       break;
484:     case 2:
485:       PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */
486:       break;
487:     case 3:
488:       break;
489:     }
490:     PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */
491:   }

493:   { /* Additional keys and commands */
494:     char         keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = "";
495:     PetscOptions opt = ((PetscObject)viewer)->options;
496:     const char  *pre = ((PetscObject)viewer)->prefix;

498:     PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL));
499:     PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL));
500:     if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys));
501:     if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds));
502:   }

504:   /* Pause visualization */
505:   if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n"));
506:   if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n"));

508:   info->init = PETSC_TRUE;
509:   PetscFunctionReturn(PETSC_SUCCESS);
510: }

512: static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
513: {
514:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
515:   PetscInt         i;

517:   PetscFunctionBegin;
518:   for (i = 0; i < socket->nwindow; i++) {
519:     PetscCall(PetscViewerDestroy(&socket->window[i]));
520:     PetscCall(PetscFree(socket->windowtitle[i]));
521:     PetscCall(PetscFree(socket->fec_type[i]));
522:     PetscCall(PetscObjectDestroy(&socket->Ufield[i]));
523:   }
524:   PetscCall(PetscFree(socket->name));
525:   PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield));
526:   PetscCall(PetscFree(socket->fmt));
527:   PetscCall(PetscViewerDestroy(&socket->meshwindow));
528:   PetscCall(PetscObjectDestroy(&socket->dm));
529:   if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));

531:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL));
532:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL));
533:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL));
534:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
535:   PetscCall(PetscFree(socket));
536:   viewer->data = NULL;
537:   PetscFunctionReturn(PETSC_SUCCESS);
538: }

540: static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject)
541: {
542:   PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
543:   PetscInt         nsizes = 2, prec = PETSC_DECIDE;
544:   PetscBool        set;

546:   PetscFunctionBegin;
547:   PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options");
548:   PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set));
549:   if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec));
550:   PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set));
551:   if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0];
552:   PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL));
553:   PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, &set));
554:   PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, &set));
555:   PetscOptionsHeadEnd();
556:   PetscFunctionReturn(PETSC_SUCCESS);
557: }

559: static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
560: {
561:   char            *sport  = NULL;
562:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

564:   PetscFunctionBegin;
565:   socket->type = PETSC_VIEWER_GLVIS_DUMP;
566:   /* we accept localhost^port */
567:   PetscCall(PetscFree(socket->name));
568:   PetscCall(PetscStrallocpy(name, &socket->name));
569:   PetscCall(PetscStrchr(socket->name, '^', &sport));
570:   if (sport) {
571:     PetscInt       port = 19916;
572:     size_t         len;
573:     PetscErrorCode ierr;

575:     *sport++ = 0;
576:     PetscCall(PetscStrlen(sport, &len));
577:     ierr = PetscOptionsStringToInt(sport, &port);
578:     if (PetscUnlikely(ierr)) {
579:       socket->port = 19916;
580:     } else {
581:       socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916;
582:     }
583:     socket->type = PETSC_VIEWER_GLVIS_SOCKET;
584:   }
585:   PetscFunctionReturn(PETSC_SUCCESS);
586: }

588: /*@C
589:   PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`

591:   Collective; No Fortran Support

593:   Input Parameters:
594: + comm - the MPI communicator
595: . type - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file
596: . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations
597: - port - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP`

599:   Output Parameter:
600: . viewer - the `PetscViewer` object

602:   Options Database Keys:
603: + -glvis_precision <precision> - Sets number of digits for floating point values
604: . -glvis_size <width,height>   - Sets the window size (in pixels)
605: . -glvis_pause <pause>         - Sets time (in seconds) that the program pauses after each visualization
606:        (0 is default, -1 implies every visualization)
607: . -glvis_keys                  - Additional keys to configure visualization
608: - -glvis_exec                  - Additional commands to configure visualization

610:   Level: beginner

612: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType`
613: @*/
614: PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer)
615: {
616:   PetscViewerGLVis socket;

618:   PetscFunctionBegin;
619:   PetscCall(PetscViewerCreate(comm, viewer));
620:   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));

622:   socket       = (PetscViewerGLVis)((*viewer)->data);
623:   socket->type = type;
624:   if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
625:     PetscCall(PetscFree(socket->name));
626:     PetscCall(PetscStrallocpy(name, &socket->name));
627:   }
628:   socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;

630:   PetscCall(PetscViewerSetFromOptions(*viewer));
631:   PetscFunctionReturn(PETSC_SUCCESS);
632: }

634: /*@C
635:   PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.

637:   Collective; No Fortran Support

639:   Input Parameter:
640: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`

642:   Environmental variables:
643: + `PETSC_VIEWER_GLVIS_FILENAME` - output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`)
644: . `PETSC_VIEWER_GLVIS_HOSTNAME` - machine where the GLVis server is listening (defaults to localhost)
645: - `PETSC_VIEWER_GLVIS_PORT`     - port opened by the GLVis server (defaults to 19916)

647:   Level: intermediate

649:   Note:
650:   Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return
651:   an error code.  It is usually used in the form
652: $       XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));

654: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
655: @*/
656: PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm)
657: {
658:   PetscErrorCode       ierr;
659:   PetscBool            flg;
660:   PetscViewer          viewer;
661:   PetscViewerGLVisType type;
662:   char                 fname[PETSC_MAX_PATH_LEN], sport[16];
663:   PetscInt             port = 19916; /* default for GLVis */

665:   PetscFunctionBegin;
666:   ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg);
667:   if (ierr) {
668:     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
669:     PetscFunctionReturn(NULL);
670:   }
671:   if (!flg) {
672:     type = PETSC_VIEWER_GLVIS_SOCKET;
673:     ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg);
674:     if (ierr) {
675:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
676:       PetscFunctionReturn(NULL);
677:     }
678:     if (!flg) {
679:       ierr = PetscStrncpy(fname, "localhost", sizeof(fname));
680:       if (ierr) {
681:         ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
682:         PetscFunctionReturn(NULL);
683:       }
684:     }
685:     ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg);
686:     if (ierr) {
687:       ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
688:       PetscFunctionReturn(NULL);
689:     }
690:     if (flg) {
691:       ierr = PetscOptionsStringToInt(sport, &port);
692:       if (ierr) {
693:         ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
694:         PetscFunctionReturn(NULL);
695:       }
696:     }
697:   } else {
698:     type = PETSC_VIEWER_GLVIS_DUMP;
699:   }
700:   ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer);
701:   if (ierr) {
702:     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
703:     PetscFunctionReturn(NULL);
704:   }

706:   ((PetscObject)viewer)->persistent = PETSC_TRUE;

708:   ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
709:   if (ierr) {
710:     ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
711:     PetscFunctionReturn(NULL);
712:   }
713:   PetscFunctionReturn(viewer);
714: }

716: PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
717: {
718:   PetscViewerGLVis socket;

720:   PetscFunctionBegin;
721:   PetscCall(PetscNew(&socket));

723:   /* defaults to socket viewer */
724:   PetscCall(PetscStrallocpy("localhost", &socket->name));
725:   socket->port  = 19916; /* GLVis default listening port */
726:   socket->type  = PETSC_VIEWER_GLVIS_SOCKET;
727:   socket->pause = 0; /* just pause the first time */

729:   socket->windowsizes[0] = 600;
730:   socket->windowsizes[1] = 600;

732:   /* defaults to full precision */
733:   PetscCall(PetscStrallocpy(" %g", &socket->fmt));

735:   viewer->data                = (void *)socket;
736:   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
737:   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;

739:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis));
740:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis));
741:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis));
742:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis));
743:   PetscFunctionReturn(PETSC_SUCCESS);
744: }

746: /* this is a private implementation of a SOCKET with ASCII data format
747:    GLVis does not currently handle binary socket streams */
748: #if defined(PETSC_HAVE_UNISTD_H)
749:   #include <unistd.h>
750: #endif

752: #ifndef PETSC_HAVE_WINDOWS_H
753: static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);

755: static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
756: {
757:   FILE *stream;

759:   PetscFunctionBegin;
760:   PetscCall(PetscViewerASCIIGetPointer(viewer, &stream));
761:   if (stream) {
762:     int retv = fclose(stream);
763:     PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream");
764:   }
765:   PetscCall(PetscViewerDestroy_ASCII(viewer));
766:   PetscFunctionReturn(PETSC_SUCCESS);
767: }
768: #endif

770: /*
771:     This attempts to return a NULL viewer if it is unable to open a socket connection.

773:      The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.

775:      The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value
776:      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
777: */
778: static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer)
779: {
780: #if defined(PETSC_HAVE_WINDOWS_H)
781:   PetscFunctionBegin;
782:   SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows");
783: #else
784:   FILE          *stream = NULL;
785:   int            fd     = 0;
786:   PetscErrorCode ierr;

788:   PetscFunctionBegin;
789:   PetscAssertPointer(hostname, 2);
790:   PetscAssertPointer(viewer, 4);
791:   #if defined(PETSC_USE_SOCKET_VIEWER)
792:   ierr = PetscOpenSocket(hostname, port, &fd);
793:   #else
794:   SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
795:   #endif
796:   /*
797:      The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
798:         The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value
799:      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
800:    */
801:   if (PetscUnlikely(ierr)) {
802:     PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
803:     *viewer = NULL;
804:     PetscFunctionReturn(PETSC_SUCCESS);
805:   } else {
806:     PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
807:   }
808:   stream = fdopen(fd, "w"); /* Not possible on Windows */
809:   PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
810:   PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
811:   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
812:   (*viewer)->ops->destroy  = PetscViewerDestroy_ASCII_Socket;
813: #endif
814:   PetscFunctionReturn(PETSC_SUCCESS);
815: }

817: #if !defined(PETSC_MISSING_SIGPIPE)

819:   #include <signal.h>

821:   #if defined(PETSC_HAVE_WINDOWS_H)
822:     #define PETSC_DEVNULL "NUL"
823:   #else
824:     #define PETSC_DEVNULL "/dev/null"
825:   #endif

827: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;

829: static void (*PetscGLVisSigHandler_save)(int) = NULL;

831: static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
832: {
833:   PetscGLVisBrokenPipe = PETSC_TRUE;
834:   #if !defined(PETSC_MISSING_SIG_IGN)
835:   signal(SIGPIPE, SIG_IGN);
836:   #endif
837: }

839: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
840: {
841:   PetscFunctionBegin;
842:   PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
843:   PetscGLVisBrokenPipe      = PETSC_FALSE;
844:   PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
845:   PetscFunctionReturn(PETSC_SUCCESS);
846: }

848: PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
849: {
850:   PetscBool flag, brokenpipe;

852:   PetscFunctionBegin;
853:   flag = PetscGLVisBrokenPipe;
854:   PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
855:   if (brokenpipe) {
856:     FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
857:     PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
858:     PetscCall(PetscViewerASCIISetFILE(*win, null));
859:     PetscCall(PetscViewerDestroy(win));
860:     if (sock) (void)fclose(sock);
861:   }
862:   (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
863:   PetscGLVisSigHandler_save = NULL;
864:   PetscGLVisBrokenPipe      = PETSC_FALSE;
865:   PetscFunctionReturn(PETSC_SUCCESS);
866: }

868: #else

870: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
871: {
872:   PetscFunctionBegin;
873:   PetscFunctionReturn(PETSC_SUCCESS);
874: }

876: PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
877: {
878:   PetscFunctionBegin;
879:   PetscFunctionReturn(PETSC_SUCCESS);
880: }

882: #endif