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(PetscObjectContainerCompose((PetscObject)window, "_glvis_info_container", info, PetscViewerGLVisInfoDestroy_Private));
199:   } else {
200:     PetscCall(PetscContainerGetPointer(container, (void **)&info));
201:   }
202:   PetscCall(PetscFree(info->fmt));
203:   PetscCall(PetscStrallocpy(socket->fmt, &info->fmt));
204:   PetscFunctionReturn(PETSC_SUCCESS);
205: }

207: static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view)
208: {
209:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
210:   PetscViewer      window = NULL;
211:   PetscBool        ldis, dis;

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

226: PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer)
227: {
228:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

230:   PetscFunctionBegin;
231:   if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause));
232:   PetscFunctionReturn(PETSC_SUCCESS);
233: }

235: /* DM specific support */
236: PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm)
237: {
238:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

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

245:     PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm));
246:     PetscCheck(setupwithdm, PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
247:     PetscCall((*setupwithdm)(dm, viewer));
248:     PetscCall(PetscObjectReference(dm));
249:     socket->dm = dm;
250:   }
251:   PetscFunctionReturn(PETSC_SUCCESS);
252: }

254: PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
255: {
256:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

258:   PetscFunctionBegin;
259:   PetscAssertPointer(view, 2);
260:   if (!socket->meshwindow) {
261:     if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) {
262:       PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow));
263:     } else {
264:       size_t    len;
265:       PetscBool isstdout;

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

286: PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
287: {
288:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

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

306: PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
307: {
308:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

310:   PetscFunctionBegin;
311:   PetscAssertPointer(type, 2);
312:   *type = socket->type;
313:   PetscFunctionReturn(PETSC_SUCCESS);
314: }

316: /* 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 */
317: PetscErrorCode PetscViewerGLVisGetStatus_Internal(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
318: {
319:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

321:   PetscFunctionBegin;
322:   PetscAssertPointer(sockstatus, 2);
323:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
324:     socket->status = PETSCVIEWERGLVIS_DISCONNECTED;
325:   } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) {
326:     PetscInt  i;
327:     PetscBool lconn, conn;

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

332:     PetscCallMPI(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer)));
333:     if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
334:   }
335:   *sockstatus = socket->status;
336:   PetscFunctionReturn(PETSC_SUCCESS);
337: }

339: PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
340: {
341:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

343:   PetscFunctionBegin;
344:   *dm = socket->dm;
345:   PetscFunctionReturn(PETSC_SUCCESS);
346: }

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

352:   PetscFunctionBegin;
353:   if (nfield) *nfield = socket->nwindow;
354:   if (fec) *fec = (const char **)socket->fec_type;
355:   if (spacedim) *spacedim = socket->spacedim;
356:   if (g2lfield) *g2lfield = socket->g2lfield;
357:   if (Ufield) *Ufield = socket->Ufield;
358:   if (userctx) *userctx = socket->userctx;
359:   PetscFunctionReturn(PETSC_SUCCESS);
360: }

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

371:   PetscFunctionBegin;
373:   PetscAssertPointer(view, 3);
374:   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);
375:   status = socket->status;
376:   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);
377:   switch (status) {
378:   case PETSCVIEWERGLVIS_DISCONNECTED:
379:     PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen");
380:     if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
381:       size_t    len;
382:       PetscBool isstdout;

384:       PetscCall(PetscStrlen(socket->name, &len));
385:       PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
386:       if (!socket->name || !len || isstdout) {
387:         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid]));
388:       } else {
389:         PetscMPIInt rank;
390:         char        filename[PETSC_MAX_PATH_LEN];

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

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

426:   PetscFunctionBegin;
429:   PetscAssertPointer(view, 3);
430:   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);
431:   PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()");
432:   if (*view) {
433:     PetscCall(PetscViewerFlush(*view));
434:     PetscCall(PetscBarrier((PetscObject)viewer));
435:   }
436:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
437:     PetscCall(PetscViewerDestroy(&socket->window[wid]));
438:   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
439:     socket->window[wid] = NULL;
440:   }
441:   *view = NULL;
442:   PetscFunctionReturn(PETSC_SUCCESS);
443: }

445: /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
446: PetscErrorCode PetscViewerGLVisInitWindow_Internal(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
447: {
448:   PetscViewerGLVisInfo info;
449:   PetscContainer       container;

451:   PetscFunctionBegin;
452:   PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container));
453:   PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private");
454:   PetscCall(PetscContainerGetPointer(container, (void **)&info));
455:   if (info->init) PetscFunctionReturn(PETSC_SUCCESS);

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

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

488:   { /* Additional keys and commands */
489:     char         keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = "";
490:     PetscOptions opt = ((PetscObject)viewer)->options;
491:     const char  *pre = ((PetscObject)viewer)->prefix;

493:     PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL));
494:     PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL));
495:     if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys));
496:     if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds));
497:   }

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

503:   info->init = PETSC_TRUE;
504:   PetscFunctionReturn(PETSC_SUCCESS);
505: }

507: static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
508: {
509:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
510:   PetscInt         i;

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

526:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL));
527:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL));
528:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL));
529:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
530:   PetscCall(PetscFree(socket));
531:   viewer->data = NULL;
532:   PetscFunctionReturn(PETSC_SUCCESS);
533: }

535: static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems PetscOptionsObject)
536: {
537:   PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
538:   PetscInt         nsizes = 2, prec = PETSC_DECIDE;
539:   PetscBool        set;

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

554: static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
555: {
556:   char            *sport  = NULL;
557:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

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

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

583: /*@
584:   PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`

586:   Collective; No Fortran Support

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

594:   Output Parameter:
595: . viewer - the `PetscViewer` object

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

605:   Level: beginner

607: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType`
608: @*/
609: PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer)
610: {
611:   PetscViewerGLVis socket;

613:   PetscFunctionBegin;
614:   PetscCall(PetscViewerCreate(comm, viewer));
615:   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));

617:   socket       = (PetscViewerGLVis)((*viewer)->data);
618:   socket->type = type;
619:   if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
620:     PetscCall(PetscFree(socket->name));
621:     PetscCall(PetscStrallocpy(name, &socket->name));
622:   }
623:   socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;

625:   PetscCall(PetscViewerSetFromOptions(*viewer));
626:   PetscFunctionReturn(PETSC_SUCCESS);
627: }

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

632:   Collective; No Fortran Support

634:   Input Parameter:
635: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`

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

642:   Level: intermediate

644:   Note:
645:   Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return
646:   an error code.  It is usually used in the form
647: .vb
648:        XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));
649: .ve

651:   Developer Note:
652:   How come this viewer is not stashed as an attribute in the MPI communicator?

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

664:   PetscFunctionBegin;
665:   PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg));
666:   if (!flg) {
667:     type = PETSC_VIEWER_GLVIS_SOCKET;
668:     PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg));
669:     if (!flg) PetscCallNull(PetscStrncpy(fname, "localhost", sizeof(fname)));
670:     PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg));
671:     if (flg) PetscCallNull(PetscOptionsStringToInt(sport, &port));
672:   } else {
673:     type = PETSC_VIEWER_GLVIS_DUMP;
674:   }
675:   PetscCallNull(PetscViewerGLVisOpen(comm, type, fname, port, &viewer));
676:   PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer));
677:   PetscFunctionReturn(viewer);
678: }

680: PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
681: {
682:   PetscViewerGLVis socket;

684:   PetscFunctionBegin;
685:   PetscCall(PetscNew(&socket));

687:   /* defaults to socket viewer */
688:   PetscCall(PetscStrallocpy("localhost", &socket->name));
689:   socket->port  = 19916; /* GLVis default listening port */
690:   socket->type  = PETSC_VIEWER_GLVIS_SOCKET;
691:   socket->pause = 0; /* just pause the first time */

693:   socket->windowsizes[0] = 600;
694:   socket->windowsizes[1] = 600;

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

699:   viewer->data                = (void *)socket;
700:   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
701:   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;

703:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis));
704:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis));
705:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis));
706:   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis));
707:   PetscFunctionReturn(PETSC_SUCCESS);
708: }

710: /* this is a private implementation of a SOCKET with ASCII data format
711:    GLVis does not currently handle binary socket streams */
712: #if defined(PETSC_HAVE_UNISTD_H)
713:   #include <unistd.h>
714: #endif

716: #ifndef PETSC_HAVE_WINDOWS_H
717: static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);

719: static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
720: {
721:   FILE *stream;

723:   PetscFunctionBegin;
724:   PetscCall(PetscViewerASCIIGetPointer(viewer, &stream));
725:   if (stream) {
726:     int retv = fclose(stream);
727:     PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream");
728:   }
729:   PetscCall(PetscViewerDestroy_ASCII(viewer));
730:   PetscFunctionReturn(PETSC_SUCCESS);
731: }
732: #endif

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

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

739:      The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value
740:      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
741: */
742: static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer)
743: {
744: #if defined(PETSC_HAVE_WINDOWS_H)
745:   PetscFunctionBegin;
746:   SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows");
747: #else
748:   FILE          *stream = NULL;
749:   int            fd     = 0;
750:   PetscErrorCode ierr;
751:   PetscMPIInt    iport;

753:   PetscFunctionBegin;
754:   PetscAssertPointer(hostname, 2);
755:   PetscAssertPointer(viewer, 4);
756:   PetscCall(PetscMPIIntCast(port, &iport));
757:   #if defined(PETSC_USE_SOCKET_VIEWER)
758:   ierr = PetscOpenSocket(hostname, iport, &fd);
759:   #else
760:   SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
761:   #endif
762:   /*
763:      The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
764:         The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value
765:      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
766:    */
767:   if (PetscUnlikely(ierr)) {
768:     PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
769:     *viewer = NULL;
770:     PetscFunctionReturn(PETSC_SUCCESS);
771:   } else {
772:     PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
773:   }
774:   stream = fdopen(fd, "w"); /* Not possible on Windows */
775:   PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
776:   PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
777:   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
778:   (*viewer)->ops->destroy  = PetscViewerDestroy_ASCII_Socket;
779: #endif
780:   PetscFunctionReturn(PETSC_SUCCESS);
781: }

783: #if !defined(PETSC_MISSING_SIGPIPE)

785:   #include <signal.h>

787:   #if defined(PETSC_HAVE_WINDOWS_H)
788:     #define PETSC_DEVNULL "NUL"
789:   #else
790:     #define PETSC_DEVNULL "/dev/null"
791:   #endif

793: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;

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

797: static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
798: {
799:   PetscGLVisBrokenPipe = PETSC_TRUE;
800:   #if !defined(PETSC_MISSING_SIG_IGN)
801:   signal(SIGPIPE, SIG_IGN);
802:   #endif
803: }

805: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
806: {
807:   PetscFunctionBegin;
808:   PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
809:   PetscGLVisBrokenPipe      = PETSC_FALSE;
810:   PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
811:   PetscFunctionReturn(PETSC_SUCCESS);
812: }

814: PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
815: {
816:   PetscBool flag, brokenpipe;

818:   PetscFunctionBegin;
819:   flag = PetscGLVisBrokenPipe;
820:   PetscCallMPI(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
821:   if (brokenpipe) {
822:     FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
823:     PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
824:     PetscCall(PetscViewerASCIISetFILE(*win, null));
825:     PetscCall(PetscViewerDestroy(win));
826:     if (sock) (void)fclose(sock);
827:   }
828:   (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
829:   PetscGLVisSigHandler_save = NULL;
830:   PetscGLVisBrokenPipe      = PETSC_FALSE;
831:   PetscFunctionReturn(PETSC_SUCCESS);
832: }

834: #else

836: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
837: {
838:   PetscFunctionBegin;
839:   PetscFunctionReturn(PETSC_SUCCESS);
840: }

842: PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
843: {
844:   PetscFunctionBegin;
845:   PetscFunctionReturn(PETSC_SUCCESS);
846: }

848: #endif