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: {
 49:   PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec));
 50:   return 0;
 51: }

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

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

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

 70:   Logically Collective

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

 76:   Level: beginner

 78: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
 79: @*/
 80: PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id)
 81: {
 84:   PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id));
 85:   return 0;
 86: }

 88: static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id)
 89: {
 90:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

 92:   socket->snapid = id;
 93:   return 0;
 94: }

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

 99:   Logically Collective

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

111:   Notes:
112:     g2lfields is called on the vector V to be visualized in order to extract the relevant dofs to be put in Vfield[], as
113: .vb
114:   g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx).
115: .ve

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

120:   Level: intermediate

122: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscObjectSetName()`
123: @*/
124: 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 *))
125: {
132:   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));
133:   return 0;
134: }

136: 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 *))
137: {
138:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
139:   PetscInt         i;

142:   if (!socket->nwindow) {
143:     socket->nwindow = nfields;

145:     PetscCalloc5(nfields, &socket->window, nfields, &socket->windowtitle, nfields, &socket->fec_type, nfields, &socket->spacedim, nfields, &socket->Ufield);
146:     for (i = 0; i < nfields; i++) {
147:       const char *name;

149:       PetscObjectGetName(Vfield[i], &name);
150:       PetscStrallocpy(name, &socket->windowtitle[i]);
151:       PetscStrallocpy(fec_type[i], &socket->fec_type[i]);
152:       PetscObjectReference(Vfield[i]);
153:       socket->Ufield[i]   = Vfield[i];
154:       socket->spacedim[i] = dim[i];
155:     }
156:   }
157:   /* number of fields are not allowed to vary */
159:   socket->g2lfield = g2l;
160:   if (socket->destroyctx && socket->userctx) (*socket->destroyctx)(socket->userctx);
161:   socket->userctx    = ctx;
162:   socket->destroyctx = destroyctx;
163:   return 0;
164: }

166: static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr)
167: {
168:   PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr;

170:   PetscFree(info->fmt);
171:   PetscFree(info);
172:   return 0;
173: }

175: /* we can decide to prevent specific processes from using the viewer */
176: static PetscErrorCode PetscViewerGLVisAttachInfo_Private(PetscViewer viewer, PetscViewer window)
177: {
178:   PetscViewerGLVis     socket = (PetscViewerGLVis)viewer->data;
179:   PetscContainer       container;
180:   PetscViewerGLVisInfo info;

182:   PetscObjectQuery((PetscObject)window, "_glvis_info_container", (PetscObject *)&container);
183:   if (!container) {
184:     PetscNew(&info);
185:     info->enabled = PETSC_TRUE;
186:     info->init    = PETSC_FALSE;
187:     info->size[0] = socket->windowsizes[0];
188:     info->size[1] = socket->windowsizes[1];
189:     info->pause   = socket->pause;
190:     PetscContainerCreate(PetscObjectComm((PetscObject)window), &container);
191:     PetscContainerSetPointer(container, (void *)info);
192:     PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private);
193:     PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container);
194:     PetscContainerDestroy(&container);
195:   } else {
196:     PetscContainerGetPointer(container, (void **)&info);
197:   }
198:   PetscFree(info->fmt);
199:   PetscStrallocpy(socket->fmt, &info->fmt);
200:   return 0;
201: }

203: static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view)
204: {
205:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
206:   PetscViewer      window = NULL;
207:   PetscBool        ldis, dis;

209:   PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window);
210:   /* if we could not establish a connection, we disable the socket viewer on all MPI ranks */
211:   ldis = !viewer ? PETSC_TRUE : PETSC_FALSE;
212:   MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer));
213:   if (dis) {
214:     socket->status = PETSCVIEWERGLVIS_DISABLED;
215:     PetscViewerDestroy(&window);
216:   }
217:   *view = window;
218:   return 0;
219: }

221: PetscErrorCode PetscViewerGLVisPause_Private(PetscViewer viewer)
222: {
223:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

225:   if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscSleep(socket->pause);
226:   return 0;
227: }

229: /* DM specific support */
230: PetscErrorCode PetscViewerGLVisSetDM_Private(PetscViewer viewer, PetscObject dm)
231: {
232:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

235:   if (!socket->dm) {
236:     PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL;

238:     PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm);
239:     if (setupwithdm) {
240:       (*setupwithdm)(dm, viewer);
241:     } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
242:     PetscObjectReference(dm);
243:     socket->dm = dm;
244:   }
245:   return 0;
246: }

248: PetscErrorCode PetscViewerGLVisGetDMWindow_Private(PetscViewer viewer, PetscViewer *view)
249: {
250:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

253:   if (!socket->meshwindow) {
254:     if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) {
255:       PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow);
256:     } else {
257:       size_t    len;
258:       PetscBool isstdout;

260:       PetscStrlen(socket->name, &len);
261:       PetscStrcmp(socket->name, "stdout", &isstdout);
262:       if (!socket->name || !len || isstdout) {
263:         PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow);
264:       } else {
265:         PetscMPIInt rank;
266:         char        filename[PETSC_MAX_PATH_LEN];
267:         MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank);
268:         PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank);
269:         PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow);
270:       }
271:     }
272:     if (socket->meshwindow) PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS);
273:   }
274:   if (socket->meshwindow) PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow);
275:   *view = socket->meshwindow;
276:   return 0;
277: }

279: PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer, PetscViewer *view)
280: {
281:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

285:   if (*view) {
286:     PetscViewerFlush(*view);
287:     PetscBarrier((PetscObject)viewer);
288:   }
289:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
290:     PetscViewerDestroy(&socket->meshwindow);
291:   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
292:     socket->meshwindow = NULL;
293:   }
294:   *view = NULL;
295:   return 0;
296: }

298: PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer, PetscViewerGLVisType *type)
299: {
300:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

303:   *type = socket->type;
304:   return 0;
305: }

307: /* 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 */
308: PetscErrorCode PetscViewerGLVisGetStatus_Private(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
309: {
310:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

313:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
314:     socket->status = PETSCVIEWERGLVIS_DISCONNECTED;
315:   } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) {
316:     PetscInt  i;
317:     PetscBool lconn, conn;

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

322:     MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer));
323:     if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
324:   }
325:   *sockstatus = socket->status;
326:   return 0;
327: }

329: PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject *dm)
330: {
331:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

333:   *dm = socket->dm;
334:   return 0;
335: }

337: PetscErrorCode PetscViewerGLVisGetFields_Private(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx)
338: {
339:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

341:   if (nfield) *nfield = socket->nwindow;
342:   if (fec) *fec = (const char **)socket->fec_type;
343:   if (spacedim) *spacedim = socket->spacedim;
344:   if (g2lfield) *g2lfield = socket->g2lfield;
345:   if (Ufield) *Ufield = socket->Ufield;
346:   if (userctx) *userctx = socket->userctx;
347:   return 0;
348: }

350: /* accessor routines for the viewer windows:
351:    PETSC_VIEWER_GLVIS_DUMP   : it returns a new viewer every time
352:    PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done.
353: */
354: PetscErrorCode PetscViewerGLVisGetWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view)
355: {
356:   PetscViewerGLVis       socket = (PetscViewerGLVis)viewer->data;
357:   PetscViewerGLVisStatus status;

362:   status = socket->status;
364:   switch (status) {
365:   case PETSCVIEWERGLVIS_DISCONNECTED:
367:     if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
368:       size_t    len;
369:       PetscBool isstdout;

371:       PetscStrlen(socket->name, &len);
372:       PetscStrcmp(socket->name, "stdout", &isstdout);
373:       if (!socket->name || !len || isstdout) {
374:         PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid]);
375:       } else {
376:         PetscMPIInt rank;
377:         char        filename[PETSC_MAX_PATH_LEN];

379:         MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank);
380:         PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank);
381:         PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid]);
382:       }
383:     } else {
384:       PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid]);
385:     }
386:     if (socket->window[wid]) PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS);
387:     *view = socket->window[wid];
388:     break;
389:   case PETSCVIEWERGLVIS_CONNECTED:
390:     *view = socket->window[wid];
391:     break;
392:   case PETSCVIEWERGLVIS_DISABLED:
393:     *view = NULL;
394:     break;
395:   default:
396:     SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status);
397:   }
398:   if (*view) PetscViewerGLVisAttachInfo_Private(viewer, *view);
399:   return 0;
400: }

402: /* Restore the window viewer
403:    PETSC_VIEWER_GLVIS_DUMP  : destroys the temporary created ASCII viewer used for dumping
404:    PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer.
405:                  - it the returned window viewer is NULL, assumes something went wrong
406:                    with the socket (i.e. SIGPIPE when a user closes the popup window)
407:                    and that the caller already handled it (see VecView_GLVis).
408: */
409: PetscErrorCode PetscViewerGLVisRestoreWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view)
410: {
411:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

418:   if (*view) {
419:     PetscViewerFlush(*view);
420:     PetscBarrier((PetscObject)viewer);
421:   }
422:   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
423:     PetscViewerDestroy(&socket->window[wid]);
424:   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
425:     socket->window[wid] = NULL;
426:   }
427:   *view = NULL;
428:   return 0;
429: }

431: /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
432: PetscErrorCode PetscViewerGLVisInitWindow_Private(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
433: {
434:   PetscViewerGLVisInfo info;
435:   PetscContainer       container;

437:   PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container);
439:   PetscContainerGetPointer(container, (void **)&info);
440:   if (info->init) return 0;

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

446:   /* Configure default view */
447:   if (mesh) {
448:     switch (dim) {
449:     case 1:
450:       PetscViewerASCIIPrintf(viewer, "keys m\n"); /* show mesh */
451:       break;
452:     case 2:
453:       PetscViewerASCIIPrintf(viewer, "keys m\n"); /* show mesh */
454:       break;
455:     case 3: /* TODO: decide default view in 3D */
456:       break;
457:     }
458:   } else {
459:     PetscViewerASCIIPrintf(viewer, "keys cm\n"); /* show colorbar and mesh */
460:     switch (dim) {
461:     case 1:
462:       PetscViewerASCIIPrintf(viewer, "keys RRjl\n"); /* set to 1D (side view), turn off perspective and light */
463:       break;
464:     case 2:
465:       PetscViewerASCIIPrintf(viewer, "keys Rjl\n"); /* set to 2D (top view), turn off perspective and light */
466:       break;
467:     case 3:
468:       break;
469:     }
470:     PetscViewerASCIIPrintf(viewer, "autoscale value\n"); /* update value-range; keep mesh-extents fixed */
471:   }

473:   { /* Additional keys and commands */
474:     char         keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = "";
475:     PetscOptions opt = ((PetscObject)viewer)->options;
476:     const char  *pre = ((PetscObject)viewer)->prefix;

478:     PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL);
479:     PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL);
480:     if (keys[0]) PetscViewerASCIIPrintf(viewer, "keys %s\n", keys);
481:     if (cmds[0]) PetscViewerASCIIPrintf(viewer, "%s\n", cmds);
482:   }

484:   /* Pause visualization */
485:   if (!mesh && info->pause == -1) PetscViewerASCIIPrintf(viewer, "autopause 1\n");
486:   if (!mesh && info->pause == 0) PetscViewerASCIIPrintf(viewer, "pause\n");

488:   info->init = PETSC_TRUE;
489:   return 0;
490: }

492: static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
493: {
494:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
495:   PetscInt         i;

497:   for (i = 0; i < socket->nwindow; i++) {
498:     PetscViewerDestroy(&socket->window[i]);
499:     PetscFree(socket->windowtitle[i]);
500:     PetscFree(socket->fec_type[i]);
501:     PetscObjectDestroy(&socket->Ufield[i]);
502:   }
503:   PetscFree(socket->name);
504:   PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield);
505:   PetscFree(socket->fmt);
506:   PetscViewerDestroy(&socket->meshwindow);
507:   PetscObjectDestroy(&socket->dm);
508:   if (socket->destroyctx && socket->userctx) (*socket->destroyctx)(socket->userctx);

510:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL);
511:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL);
512:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL);
513:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL);
514:   PetscFree(socket);
515:   viewer->data = NULL;
516:   return 0;
517: }

519: static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject)
520: {
521:   PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
522:   PetscInt         nsizes = 2, prec = PETSC_DECIDE;
523:   PetscBool        set;

525:   PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options");
526:   PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set);
527:   if (set) PetscViewerGLVisSetPrecision(v, prec);
528:   PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set);
529:   if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0];
530:   PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL);
531:   PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, NULL);
532:   PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, NULL);
533:   PetscOptionsHeadEnd();
534:   return 0;
535: }

537: static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
538: {
539:   char            *sport;
540:   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;

542:   socket->type = PETSC_VIEWER_GLVIS_DUMP;
543:   /* we accept localhost^port */
544:   PetscFree(socket->name);
545:   PetscStrallocpy(name, &socket->name);
546:   PetscStrchr(socket->name, '^', &sport);
547:   if (sport) {
548:     PetscInt       port = 19916;
549:     size_t         len;

552:     *sport++ = 0;
553:     PetscStrlen(sport, &len);
554:     PetscOptionsStringToInt(sport, &port);
555:     if (PetscUnlikely(ierr)) {
556:       socket->port = 19916;
557:     } else {
558:       socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916;
559:     }
560:     socket->type = PETSC_VIEWER_GLVIS_SOCKET;
561:   }
562:   return 0;
563: }

565: /*@C
566:   PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`

568:   Collective; No Fortran Support

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

576:   Output Parameters:
577: -  viewer    - the `PetscViewer` object

579:   Options Database Keys:
580: +  -glvis_precision <precision> - Sets number of digits for floating point values
581: .  -glvis_size <width,height> - Sets the window size (in pixels)
582: .  -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization
583:        (0 is default, -1 implies every visualization)
584: .  -glvis_keys - Additional keys to configure visualization
585: -  -glvis_exec - Additional commands to configure visualization

587:   Level: beginner

589: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType`
590: @*/
591: PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer)
592: {
593:   PetscViewerGLVis socket;

595:   PetscViewerCreate(comm, viewer);
596:   PetscViewerSetType(*viewer, PETSCVIEWERGLVIS);

598:   socket       = (PetscViewerGLVis)((*viewer)->data);
599:   socket->type = type;
600:   if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
601:     PetscFree(socket->name);
602:     PetscStrallocpy(name, &socket->name);
603:   }
604:   socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;

606:   PetscViewerSetFromOptions(*viewer);
607:   return 0;
608: }

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

613:   Collective; No Fortran Support

615:   Input Parameter:
616: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`

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

623:   Level: intermediate

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

630: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
631: @*/
632: PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm)
633: {
634:   PetscErrorCode       ierr;
635:   PetscBool            flg;
636:   PetscViewer          viewer;
637:   PetscViewerGLVisType type;
638:   char                 fname[PETSC_MAX_PATH_LEN], sport[16];
639:   PetscInt             port = 19916; /* default for GLVis */

641:   PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg);
642:   if (ierr) {
643:     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
644:     return NULL;
645:   }
646:   if (!flg) {
647:     type = PETSC_VIEWER_GLVIS_SOCKET;
648:     PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg);
649:     if (ierr) {
650:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
651:       return NULL;
652:     }
653:     if (!flg) {
654:       PetscStrcpy(fname, "localhost");
655:       if (ierr) {
656:         PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
657:         return NULL;
658:       }
659:     }
660:     PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg);
661:     if (ierr) {
662:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
663:       return NULL;
664:     }
665:     if (flg) {
666:       PetscOptionsStringToInt(sport, &port);
667:       if (ierr) {
668:         PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
669:         return NULL;
670:       }
671:     }
672:   } else {
673:     type = PETSC_VIEWER_GLVIS_DUMP;
674:   }
675:   PetscViewerGLVisOpen(comm, type, fname, port, &viewer);
676:   if (ierr) {
677:     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
678:     return NULL;
679:   }
680:   PetscObjectRegisterDestroy((PetscObject)viewer);
681:   if (ierr) {
682:     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
683:     return NULL;
684:   }
685:   return viewer;
686: }

688: PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
689: {
690:   PetscViewerGLVis socket;

692:   PetscNew(&socket);

694:   /* defaults to socket viewer */
695:   PetscStrallocpy("localhost", &socket->name);
696:   socket->port  = 19916; /* GLVis default listening port */
697:   socket->type  = PETSC_VIEWER_GLVIS_SOCKET;
698:   socket->pause = 0; /* just pause the first time */

700:   socket->windowsizes[0] = 600;
701:   socket->windowsizes[1] = 600;

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

706:   viewer->data                = (void *)socket;
707:   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
708:   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;

710:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis);
711:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis);
712:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis);
713:   PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis);
714:   return 0;
715: }

717: /* this is a private implementation of a SOCKET with ASCII data format
718:    GLVis does not currently handle binary socket streams */
719: #if defined(PETSC_HAVE_UNISTD_H)
720:   #include <unistd.h>
721: #endif

723: #ifndef PETSC_HAVE_WINDOWS_H
724: static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);

726: static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
727: {
728:   FILE *stream;

730:   PetscViewerASCIIGetPointer(viewer, &stream);
731:   if (stream) {
732:     int retv = fclose(stream);
734:   }
735:   PetscViewerDestroy_ASCII(viewer);
736:   return 0;
737: }
738: #endif

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

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

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

759:   #if defined(PETSC_USE_SOCKET_VIEWER)
760:   PetscOpenSocket(hostname, port, &fd);
761:   #else
762:   SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
763:   #endif
764:   /*
765:      The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
766:         The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value
767:      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
768:    */
769:   if (PetscUnlikely(ierr)) {
770:     char err[1024];

772:     PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port);
773:     PetscInfo(NULL, "%s", err);
774:     *viewer = NULL;
775:     return 0;
776:   } else {
777:     char msg[1024];

779:     PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port);
780:     PetscInfo(NULL, "%s", msg);
781:   }
782:   stream = fdopen(fd, "w"); /* Not possible on Windows */
784:   PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer);
785:   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
786:   (*viewer)->ops->destroy  = PetscViewerDestroy_ASCII_Socket;
787: #endif
788:   return 0;
789: }

791: #if !defined(PETSC_MISSING_SIGPIPE)

793:   #include <signal.h>

795:   #if defined(PETSC_HAVE_WINDOWS_H)
796:     #define PETSC_DEVNULL "NUL"
797:   #else
798:     #define PETSC_DEVNULL "/dev/null"
799:   #endif

801: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;

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

805: static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
806: {
807:   PetscGLVisBrokenPipe = PETSC_TRUE;
808:   #if !defined(PETSC_MISSING_SIG_IGN)
809:   signal(SIGPIPE, SIG_IGN);
810:   #endif
811: }

813: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
814: {
816:   PetscGLVisBrokenPipe      = PETSC_FALSE;
817:   PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
818:   return 0;
819: }

821: PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
822: {
823:   PetscBool flag, brokenpipe;

825:   flag = PetscGLVisBrokenPipe;
826:   MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm);
827:   if (brokenpipe) {
828:     FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
829:     PetscViewerASCIIGetPointer(*win, &sock);
830:     PetscViewerASCIISetFILE(*win, null);
831:     PetscViewerDestroy(win);
832:     if (sock) (void)fclose(sock);
833:   }
834:   (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
835:   PetscGLVisSigHandler_save = NULL;
836:   PetscGLVisBrokenPipe      = PETSC_FALSE;
837:   return 0;
838: }

840: #else

842: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
843: {
844:   return 0;
845: }

847: PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
848: {
849:   return 0;
850: }

852: #endif