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:     if (setupwithdm) {
247:       PetscCall((*setupwithdm)(dm, viewer));
248:     } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
249:     PetscCall(PetscObjectReference(dm));
250:     socket->dm = dm;
251:   }
252:   PetscFunctionReturn(PETSC_SUCCESS);
253: }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

587:   Collective; No Fortran Support

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

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

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

606:   Level: beginner

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

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

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

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

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

633:   Collective; No Fortran Support

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

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

643:   Level: intermediate

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

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

655: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
656: @*/
657: PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm)
658: {
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:   PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg));
667:   if (!flg) {
668:     type = PETSC_VIEWER_GLVIS_SOCKET;
669:     PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg));
670:     if (!flg) { PetscCallNull(PetscStrncpy(fname, "localhost", sizeof(fname))); }
671:     PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg));
672:     if (flg) { PetscCallNull(PetscOptionsStringToInt(sport, &port)); }
673:   } else {
674:     type = PETSC_VIEWER_GLVIS_DUMP;
675:   }
676:   PetscCallNull(PetscViewerGLVisOpen(comm, type, fname, port, &viewer));
677:   PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer));
678:   PetscFunctionReturn(viewer);
679: }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

782: #if !defined(PETSC_MISSING_SIGPIPE)

784:   #include <signal.h>

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

792: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;

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

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

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

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

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

833: #else

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

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

847: #endif