xref: /petsc/src/sys/classes/viewer/impls/glvis/glvis.c (revision 0baf8eba40dbc839082666f9f7396a225d6f663c)
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, 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 }
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     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 }
254 
255 PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
256 {
257   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
258 
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;
267 
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 }
286 
287 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
288 {
289   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
290 
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 }
306 
307 PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
308 {
309   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
310 
311   PetscFunctionBegin;
312   PetscAssertPointer(type, 2);
313   *type = socket->type;
314   PetscFunctionReturn(PETSC_SUCCESS);
315 }
316 
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;
321 
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;
329 
330     for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
331       if (!socket->window[i]) lconn = PETSC_FALSE;
332 
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 }
339 
340 PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
341 {
342   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
343 
344   PetscFunctionBegin;
345   *dm = socket->dm;
346   PetscFunctionReturn(PETSC_SUCCESS);
347 }
348 
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;
352 
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 }
362 
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;
371 
372   PetscFunctionBegin;
373   PetscValidLogicalCollectiveInt(viewer, wid, 2);
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;
384 
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];
392 
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 }
415 
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;
426 
427   PetscFunctionBegin;
428   PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS);
429   PetscValidLogicalCollectiveInt(viewer, wid, 2);
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 }
445 
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;
451 
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);
457 
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));
461 
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   }
488 
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;
493 
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   }
499 
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"));
503 
504   info->init = PETSC_TRUE;
505   PetscFunctionReturn(PETSC_SUCCESS);
506 }
507 
508 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
509 {
510   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
511   PetscInt         i;
512 
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));
526 
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 }
535 
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;
541 
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 }
554 
555 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
556 {
557   char            *sport  = NULL;
558   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
559 
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;
570 
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 }
583 
584 /*@
585   PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`
586 
587   Collective; No Fortran Support
588 
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`
594 
595   Output Parameter:
596 . viewer - the `PetscViewer` object
597 
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
605 
606   Level: beginner
607 
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;
613 
614   PetscFunctionBegin;
615   PetscCall(PetscViewerCreate(comm, viewer));
616   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));
617 
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;
625 
626   PetscCall(PetscViewerSetFromOptions(*viewer));
627   PetscFunctionReturn(PETSC_SUCCESS);
628 }
629 
630 /*@C
631   PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.
632 
633   Collective; No Fortran Support
634 
635   Input Parameter:
636 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`
637 
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)
642 
643   Level: intermediate
644 
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
651 
652   Developer Note:
653   How come this viewer is not stashed as an attribute in the MPI communicator?
654 
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 */
664 
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 }
680 
681 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
682 {
683   PetscViewerGLVis socket;
684 
685   PetscFunctionBegin;
686   PetscCall(PetscNew(&socket));
687 
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 */
693 
694   socket->windowsizes[0] = 600;
695   socket->windowsizes[1] = 600;
696 
697   /* defaults to full precision */
698   PetscCall(PetscStrallocpy(" %g", &socket->fmt));
699 
700   viewer->data                = (void *)socket;
701   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
702   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
703 
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 }
710 
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
716 
717 #ifndef PETSC_HAVE_WINDOWS_H
718 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
719 
720 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
721 {
722   FILE *stream;
723 
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
734 
735 /*
736     This attempts to return a NULL viewer if it is unable to open a socket connection.
737 
738      The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
739 
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;
752   PetscMPIInt    iport;
753 
754   PetscFunctionBegin;
755   PetscAssertPointer(hostname, 2);
756   PetscAssertPointer(viewer, 4);
757   PetscCall(PetscMPIIntCast(port, &iport));
758   #if defined(PETSC_USE_SOCKET_VIEWER)
759   ierr = PetscOpenSocket(hostname, iport, &fd);
760   #else
761   SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
762   #endif
763   /*
764      The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
765         The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value
766      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
767    */
768   if (PetscUnlikely(ierr)) {
769     PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
770     *viewer = NULL;
771     PetscFunctionReturn(PETSC_SUCCESS);
772   } else {
773     PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
774   }
775   stream = fdopen(fd, "w"); /* Not possible on Windows */
776   PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
777   PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
778   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
779   (*viewer)->ops->destroy  = PetscViewerDestroy_ASCII_Socket;
780 #endif
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 #if !defined(PETSC_MISSING_SIGPIPE)
785 
786   #include <signal.h>
787 
788   #if defined(PETSC_HAVE_WINDOWS_H)
789     #define PETSC_DEVNULL "NUL"
790   #else
791     #define PETSC_DEVNULL "/dev/null"
792   #endif
793 
794 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
795 
796 static void (*PetscGLVisSigHandler_save)(int) = NULL;
797 
798 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
799 {
800   PetscGLVisBrokenPipe = PETSC_TRUE;
801   #if !defined(PETSC_MISSING_SIG_IGN)
802   signal(SIGPIPE, SIG_IGN);
803   #endif
804 }
805 
806 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
807 {
808   PetscFunctionBegin;
809   PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
810   PetscGLVisBrokenPipe      = PETSC_FALSE;
811   PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
812   PetscFunctionReturn(PETSC_SUCCESS);
813 }
814 
815 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
816 {
817   PetscBool flag, brokenpipe;
818 
819   PetscFunctionBegin;
820   flag = PetscGLVisBrokenPipe;
821   PetscCallMPI(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
822   if (brokenpipe) {
823     FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
824     PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
825     PetscCall(PetscViewerASCIISetFILE(*win, null));
826     PetscCall(PetscViewerDestroy(win));
827     if (sock) (void)fclose(sock);
828   }
829   (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
830   PetscGLVisSigHandler_save = NULL;
831   PetscGLVisBrokenPipe      = PETSC_FALSE;
832   PetscFunctionReturn(PETSC_SUCCESS);
833 }
834 
835 #else
836 
837 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
838 {
839   PetscFunctionBegin;
840   PetscFunctionReturn(PETSC_SUCCESS);
841 }
842 
843 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
844 {
845   PetscFunctionBegin;
846   PetscFunctionReturn(PETSC_SUCCESS);
847 }
848 
849 #endif
850