xref: /petsc/src/sys/classes/viewer/impls/glvis/glvis.c (revision 57d508425293f0bb93f59574d14951d8faac9af8) !
1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fdopen() */
2 
3 #include <petsc/private/viewerimpl.h> /*I   "petscviewer.h" I*/
4 #include <petsc/private/petscimpl.h>  /*I   "petscsys.h"    I*/
5 #include <petsc/private/glvisviewerimpl.h>
6 
7 /* we may eventually make this function public */
8 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm, const char *, PetscInt, PetscViewer *);
9 
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;
32 
33 /*@
34   PetscViewerGLVisSetPrecision - Set the number of digits for floating point values to be displayed
35 
36   Not Collective
37 
38   Input Parameters:
39 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
40 - prec   - the number of digits required
41 
42   Level: beginner
43 
44 .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
45 @*/
46 PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec)
47 {
48   PetscFunctionBegin;
49   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
50   PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec));
51   PetscFunctionReturn(PETSC_SUCCESS);
52 }
53 
54 static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec)
55 {
56   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
57 
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 }
68 
69 /*@
70   PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP`
71 
72   Logically Collective
73 
74   Input Parameters:
75 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
76 - id     - the current snapshot id in a time-dependent simulation
77 
78   Level: beginner
79 
80 .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
81 @*/
82 PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id)
83 {
84   PetscFunctionBegin;
85   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
86   PetscValidLogicalCollectiveInt(viewer, id, 2);
87   PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id));
88   PetscFunctionReturn(PETSC_SUCCESS);
89 }
90 
91 static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id)
92 {
93   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
94 
95   PetscFunctionBegin;
96   socket->snapid = id;
97   PetscFunctionReturn(PETSC_SUCCESS);
98 }
99 
100 /*@C
101   PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector.
102 
103   Logically Collective
104 
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
114 
115   Level: intermediate
116 
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
122 
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.
125 
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;
131   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
132   PetscValidLogicalCollectiveInt(viewer, nf, 2);
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 }
140 
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;
145 
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;
150 
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;
154 
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 }
171 
172 static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void **ptr)
173 {
174   PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)*ptr;
175 
176   PetscFunctionBegin;
177   PetscCall(PetscFree(info->fmt));
178   PetscCall(PetscFree(info));
179   PetscFunctionReturn(PETSC_SUCCESS);
180 }
181 
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;
188 
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 }
206 
207 static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view)
208 {
209   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
210   PetscViewer      window = NULL;
211   PetscBool        ldis, dis;
212 
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, MPI_C_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 }
225 
226 PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer)
227 {
228   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
229 
230   PetscFunctionBegin;
231   if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause));
232   PetscFunctionReturn(PETSC_SUCCESS);
233 }
234 
235 /* DM specific support */
236 PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm)
237 {
238   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
239 
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;
244 
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 }
253 
254 PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
255 {
256   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
257 
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;
266 
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 }
285 
286 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
287 {
288   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
289 
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 }
305 
306 PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
307 {
308   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
309 
310   PetscFunctionBegin;
311   PetscAssertPointer(type, 2);
312   *type = socket->type;
313   PetscFunctionReturn(PETSC_SUCCESS);
314 }
315 
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;
320 
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;
328 
329     for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
330       if (!socket->window[i]) lconn = PETSC_FALSE;
331 
332     PetscCallMPI(MPIU_Allreduce(&lconn, &conn, 1, MPI_C_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer)));
333     if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
334   }
335   *sockstatus = socket->status;
336   PetscFunctionReturn(PETSC_SUCCESS);
337 }
338 
339 PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
340 {
341   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
342 
343   PetscFunctionBegin;
344   *dm = socket->dm;
345   PetscFunctionReturn(PETSC_SUCCESS);
346 }
347 
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;
351 
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 }
361 
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;
370 
371   PetscFunctionBegin;
372   PetscValidLogicalCollectiveInt(viewer, wid, 2);
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;
383 
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];
391 
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 }
414 
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;
425 
426   PetscFunctionBegin;
427   PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS);
428   PetscValidLogicalCollectiveInt(viewer, wid, 2);
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 }
444 
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;
450 
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);
456 
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));
460 
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   }
487 
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;
492 
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   }
498 
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"));
502 
503   info->init = PETSC_TRUE;
504   PetscFunctionReturn(PETSC_SUCCESS);
505 }
506 
507 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
508 {
509   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
510   PetscInt         i;
511 
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));
525 
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 }
534 
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;
540 
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 }
553 
554 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
555 {
556   char            *sport  = NULL;
557   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
558 
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;
569 
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 }
582 
583 /*@
584   PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`
585 
586   Collective; No Fortran Support
587 
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`
593 
594   Output Parameter:
595 . viewer - the `PetscViewer` object
596 
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
604 
605   Level: beginner
606 
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;
612 
613   PetscFunctionBegin;
614   PetscCall(PetscViewerCreate(comm, viewer));
615   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));
616 
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;
624 
625   PetscCall(PetscViewerSetFromOptions(*viewer));
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 /*@C
630   PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.
631 
632   Collective; No Fortran Support
633 
634   Input Parameter:
635 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`
636 
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)
641 
642   Level: intermediate
643 
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
650 
651   Developer Note:
652   How come this viewer is not stashed as an attribute in the MPI communicator?
653 
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 */
663 
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 }
679 
680 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
681 {
682   PetscViewerGLVis socket;
683 
684   PetscFunctionBegin;
685   PetscCall(PetscNew(&socket));
686 
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 */
692 
693   socket->windowsizes[0] = 600;
694   socket->windowsizes[1] = 600;
695 
696   /* defaults to full precision */
697   PetscCall(PetscStrallocpy(" %g", &socket->fmt));
698 
699   viewer->data                = (void *)socket;
700   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
701   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
702 
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 }
709 
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
715 
716 #ifndef PETSC_HAVE_WINDOWS_H
717 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
718 
719 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
720 {
721   FILE *stream;
722 
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
733 
734 /*
735     This attempts to return a NULL viewer if it is unable to open a socket connection.
736 
737      The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
738 
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;
752 
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 }
782 
783 #if !defined(PETSC_MISSING_SIGPIPE)
784 
785   #include <signal.h>
786 
787   #if defined(PETSC_HAVE_WINDOWS_H)
788     #define PETSC_DEVNULL "NUL"
789   #else
790     #define PETSC_DEVNULL "/dev/null"
791   #endif
792 
793 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
794 
795 static void (*PetscGLVisSigHandler_save)(int) = NULL;
796 
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 }
804 
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 }
813 
814 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
815 {
816   PetscBool flag, brokenpipe;
817 
818   PetscFunctionBegin;
819   flag = PetscGLVisBrokenPipe;
820   PetscCallMPI(MPIU_Allreduce(&flag, &brokenpipe, 1, MPI_C_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 }
833 
834 #else
835 
836 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
837 {
838   PetscFunctionBegin;
839   PetscFunctionReturn(PETSC_SUCCESS);
840 }
841 
842 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
843 {
844   PetscFunctionBegin;
845   PetscFunctionReturn(PETSC_SUCCESS);
846 }
847 
848 #endif
849