xref: /petsc/src/sys/classes/viewer/impls/glvis/glvis.c (revision d18748622689843e8c5ce717aabfcc708e80ab43)
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: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
45 @*/
46 PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec) {
47   PetscFunctionBegin;
48   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
49   PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec));
50   PetscFunctionReturn(0);
51 }
52 
53 static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec) {
54   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
55 
56   PetscFunctionBegin;
57   PetscCall(PetscFree(socket->fmt));
58   if (prec > 0) {
59     PetscCall(PetscMalloc1(16, &socket->fmt));
60     PetscCall(PetscSNPrintf(socket->fmt, 16, " %%.%" PetscInt_FMT "e", prec));
61   } else {
62     PetscCall(PetscStrallocpy(" %g", &socket->fmt));
63   }
64   PetscFunctionReturn(0);
65 }
66 
67 /*@
68      PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP`
69 
70   Logically Collective on viewer
71 
72   Input Parameters:
73 +  viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
74 -  id     - the current snapshot id in a time-dependent simulation
75 
76   Level: beginner
77 
78 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
79 @*/
80 PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id) {
81   PetscFunctionBegin;
82   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
83   PetscValidLogicalCollectiveInt(viewer, id, 2);
84   PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id));
85   PetscFunctionReturn(0);
86 }
87 
88 static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id) {
89   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
90 
91   PetscFunctionBegin;
92   socket->snapid = id;
93   PetscFunctionReturn(0);
94 }
95 
96 /*@C
97      PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector.
98 
99   Logically Collective on viewer
100 
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
110 
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
116 
117   For vector spaces, the block size of Vfield[i] represents the vector dimension. It misses the Fortran bindings.
118   The names of the Vfield vectors will be displayed in the window title.
119 
120   Level: intermediate
121 
122 .seealso: `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   PetscFunctionBegin;
126   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1);
127   PetscValidLogicalCollectiveInt(viewer, nf, 2);
128   PetscCheck(fec_type, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "You need to provide the FiniteElementCollection names for the fields");
129   PetscValidPointer(fec_type, 3);
130   PetscValidIntPointer(dim, 4);
131   PetscValidPointer(Vfield, 6);
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   PetscFunctionReturn(0);
134 }
135 
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   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
138   PetscInt         i;
139 
140   PetscFunctionBegin;
141   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);
142   if (!socket->nwindow) {
143     socket->nwindow = nfields;
144 
145     PetscCall(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;
148 
149       PetscCall(PetscObjectGetName(Vfield[i], &name));
150       PetscCall(PetscStrallocpy(name, &socket->windowtitle[i]));
151       PetscCall(PetscStrallocpy(fec_type[i], &socket->fec_type[i]));
152       PetscCall(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 */
158   PetscCheck(nfields == socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot visualize %" PetscInt_FMT " fields using %" PetscInt_FMT " socket windows", nfields, socket->nwindow);
159   socket->g2lfield = g2l;
160   if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));
161   socket->userctx    = ctx;
162   socket->destroyctx = destroyctx;
163   PetscFunctionReturn(0);
164 }
165 
166 static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr) {
167   PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr;
168 
169   PetscFunctionBegin;
170   PetscCall(PetscFree(info->fmt));
171   PetscCall(PetscFree(info));
172   PetscFunctionReturn(0);
173 }
174 
175 /* we can decide to prevent specific processes from using the viewer */
176 static PetscErrorCode PetscViewerGLVisAttachInfo_Private(PetscViewer viewer, PetscViewer window) {
177   PetscViewerGLVis     socket = (PetscViewerGLVis)viewer->data;
178   PetscContainer       container;
179   PetscViewerGLVisInfo info;
180 
181   PetscFunctionBegin;
182   PetscCall(PetscObjectQuery((PetscObject)window, "_glvis_info_container", (PetscObject *)&container));
183   if (!container) {
184     PetscCall(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     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)window), &container));
191     PetscCall(PetscContainerSetPointer(container, (void *)info));
192     PetscCall(PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private));
193     PetscCall(PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container));
194     PetscCall(PetscContainerDestroy(&container));
195   } else {
196     PetscCall(PetscContainerGetPointer(container, (void **)&info));
197   }
198   PetscCall(PetscFree(info->fmt));
199   PetscCall(PetscStrallocpy(socket->fmt, &info->fmt));
200   PetscFunctionReturn(0);
201 }
202 
203 static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view) {
204   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
205   PetscViewer      window = NULL;
206   PetscBool        ldis, dis;
207 
208   PetscFunctionBegin;
209   PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window));
210   /* if we could not estabilish a connection, we disable the socket viewer on all MPI ranks */
211   ldis = !viewer ? PETSC_TRUE : PETSC_FALSE;
212   PetscCall(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer)));
213   if (dis) {
214     socket->status = PETSCVIEWERGLVIS_DISABLED;
215     PetscCall(PetscViewerDestroy(&window));
216   }
217   *view = window;
218   PetscFunctionReturn(0);
219 }
220 
221 PetscErrorCode PetscViewerGLVisPause_Private(PetscViewer viewer) {
222   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
223 
224   PetscFunctionBegin;
225   if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause));
226   PetscFunctionReturn(0);
227 }
228 
229 /* DM specific support */
230 PetscErrorCode PetscViewerGLVisSetDM_Private(PetscViewer viewer, PetscObject dm) {
231   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
232 
233   PetscFunctionBegin;
234   PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer");
235   if (!socket->dm) {
236     PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL;
237 
238     PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm));
239     if (setupwithdm) {
240       PetscCall((*setupwithdm)(dm, viewer));
241     } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
242     PetscCall(PetscObjectReference(dm));
243     socket->dm = dm;
244   }
245   PetscFunctionReturn(0);
246 }
247 
248 PetscErrorCode PetscViewerGLVisGetDMWindow_Private(PetscViewer viewer, PetscViewer *view) {
249   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
250 
251   PetscFunctionBegin;
252   PetscValidPointer(view, 2);
253   if (!socket->meshwindow) {
254     if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) {
255       PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow));
256     } else {
257       size_t    len;
258       PetscBool isstdout;
259 
260       PetscCall(PetscStrlen(socket->name, &len));
261       PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
262       if (!socket->name || !len || isstdout) {
263         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow));
264       } else {
265         PetscMPIInt rank;
266         char        filename[PETSC_MAX_PATH_LEN];
267         PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
268         PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank));
269         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow));
270       }
271     }
272     if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS));
273   }
274   if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow));
275   *view = socket->meshwindow;
276   PetscFunctionReturn(0);
277 }
278 
279 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer, PetscViewer *view) {
280   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
281 
282   PetscFunctionBegin;
283   PetscValidPointer(view, 2);
284   PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()");
285   if (*view) {
286     PetscCall(PetscViewerFlush(*view));
287     PetscCall(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     PetscCall(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   PetscFunctionReturn(0);
296 }
297 
298 PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer, PetscViewerGLVisType *type) {
299   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
300 
301   PetscFunctionBegin;
302   PetscValidPointer(type, 2);
303   *type = socket->type;
304   PetscFunctionReturn(0);
305 }
306 
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   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
310 
311   PetscFunctionBegin;
312   PetscValidPointer(sockstatus, 2);
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;
318 
319     for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
320       if (!socket->window[i]) lconn = PETSC_FALSE;
321 
322     PetscCall(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   PetscFunctionReturn(0);
327 }
328 
329 PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject *dm) {
330   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
331 
332   PetscFunctionBegin;
333   *dm = socket->dm;
334   PetscFunctionReturn(0);
335 }
336 
337 PetscErrorCode PetscViewerGLVisGetFields_Private(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx) {
338   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
339 
340   PetscFunctionBegin;
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   PetscFunctionReturn(0);
348 }
349 
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   PetscViewerGLVis       socket = (PetscViewerGLVis)viewer->data;
356   PetscViewerGLVisStatus status;
357 
358   PetscFunctionBegin;
359   PetscValidLogicalCollectiveInt(viewer, wid, 2);
360   PetscValidPointer(view, 3);
361   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);
362   status = socket->status;
363   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);
364   switch (status) {
365   case PETSCVIEWERGLVIS_DISCONNECTED:
366     PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen");
367     if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
368       size_t    len;
369       PetscBool isstdout;
370 
371       PetscCall(PetscStrlen(socket->name, &len));
372       PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
373       if (!socket->name || !len || isstdout) {
374         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid]));
375       } else {
376         PetscMPIInt rank;
377         char        filename[PETSC_MAX_PATH_LEN];
378 
379         PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
380         PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank));
381         PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid]));
382       }
383     } else {
384       PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid]));
385     }
386     if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS));
387     *view = socket->window[wid];
388     break;
389   case PETSCVIEWERGLVIS_CONNECTED: *view = socket->window[wid]; break;
390   case PETSCVIEWERGLVIS_DISABLED: *view = NULL; break;
391   default: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status);
392   }
393   if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view));
394   PetscFunctionReturn(0);
395 }
396 
397 /* Restore the window viewer
398    PETSC_VIEWER_GLVIS_DUMP  : destroys the temporary created ASCII viewer used for dumping
399    PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer.
400                  - it the returned window viewer is NULL, assumes something went wrong
401                    with the socket (i.e. SIGPIPE when a user closes the popup window)
402                    and that the caller already handled it (see VecView_GLVis).
403 */
404 PetscErrorCode PetscViewerGLVisRestoreWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) {
405   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
406 
407   PetscFunctionBegin;
408   PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS);
409   PetscValidLogicalCollectiveInt(viewer, wid, 2);
410   PetscValidPointer(view, 3);
411   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);
412   PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()");
413   if (*view) {
414     PetscCall(PetscViewerFlush(*view));
415     PetscCall(PetscBarrier((PetscObject)viewer));
416   }
417   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
418     PetscCall(PetscViewerDestroy(&socket->window[wid]));
419   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
420     socket->window[wid] = NULL;
421   }
422   *view = NULL;
423   PetscFunctionReturn(0);
424 }
425 
426 /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
427 PetscErrorCode PetscViewerGLVisInitWindow_Private(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name) {
428   PetscViewerGLVisInfo info;
429   PetscContainer       container;
430 
431   PetscFunctionBegin;
432   PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container));
433   PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private");
434   PetscCall(PetscContainerGetPointer(container, (void **)&info));
435   if (info->init) PetscFunctionReturn(0);
436 
437   /* Configure window */
438   if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1]));
439   if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name));
440 
441   /* Configure default view */
442   if (mesh) {
443     switch (dim) {
444     case 1:
445       PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
446       break;
447     case 2:
448       PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
449       break;
450     case 3: /* TODO: decide default view in 3D */ break;
451     }
452   } else {
453     PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */
454     switch (dim) {
455     case 1:
456       PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */
457       break;
458     case 2:
459       PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */
460       break;
461     case 3: break;
462     }
463     PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */
464   }
465 
466   { /* Additional keys and commands */
467     char         keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = "";
468     PetscOptions opt = ((PetscObject)viewer)->options;
469     const char  *pre = ((PetscObject)viewer)->prefix;
470 
471     PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL));
472     PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL));
473     if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys));
474     if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds));
475   }
476 
477   /* Pause visualization */
478   if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n"));
479   if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n"));
480 
481   info->init = PETSC_TRUE;
482   PetscFunctionReturn(0);
483 }
484 
485 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer) {
486   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
487   PetscInt         i;
488 
489   PetscFunctionBegin;
490   for (i = 0; i < socket->nwindow; i++) {
491     PetscCall(PetscViewerDestroy(&socket->window[i]));
492     PetscCall(PetscFree(socket->windowtitle[i]));
493     PetscCall(PetscFree(socket->fec_type[i]));
494     PetscCall(PetscObjectDestroy(&socket->Ufield[i]));
495   }
496   PetscCall(PetscFree(socket->name));
497   PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield));
498   PetscCall(PetscFree(socket->fmt));
499   PetscCall(PetscViewerDestroy(&socket->meshwindow));
500   PetscCall(PetscObjectDestroy(&socket->dm));
501   if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));
502 
503   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL));
504   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL));
505   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL));
506   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
507   PetscCall(PetscFree(socket));
508   viewer->data = NULL;
509   PetscFunctionReturn(0);
510 }
511 
512 static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject) {
513   PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
514   PetscInt         nsizes = 2, prec = PETSC_DECIDE;
515   PetscBool        set;
516 
517   PetscFunctionBegin;
518   PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options");
519   PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set));
520   if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec));
521   PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set));
522   if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0];
523   PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL));
524   PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, NULL));
525   PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, NULL));
526   PetscOptionsHeadEnd();
527   PetscFunctionReturn(0);
528 }
529 
530 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) {
531   char            *sport;
532   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
533 
534   PetscFunctionBegin;
535   socket->type = PETSC_VIEWER_GLVIS_DUMP;
536   /* we accept localhost^port */
537   PetscCall(PetscFree(socket->name));
538   PetscCall(PetscStrallocpy(name, &socket->name));
539   PetscCall(PetscStrchr(socket->name, '^', &sport));
540   if (sport) {
541     PetscInt       port = 19916;
542     size_t         len;
543     PetscErrorCode ierr;
544 
545     *sport++ = 0;
546     PetscCall(PetscStrlen(sport, &len));
547     ierr = PetscOptionsStringToInt(sport, &port);
548     if (PetscUnlikely(ierr)) {
549       socket->port = 19916;
550     } else {
551       socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916;
552     }
553     socket->type = PETSC_VIEWER_GLVIS_SOCKET;
554   }
555   PetscFunctionReturn(0);
556 }
557 
558 /*@C
559   PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`
560 
561   Collective
562 
563   Input Parameters:
564 +  comm      - the MPI communicator
565 .  type      - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file
566 .  name      - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations
567 -  port      - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP`
568 
569   Output Parameters:
570 -  viewer    - the `PetscViewer` object
571 
572   Options Database Keys:
573 +  -glvis_precision <precision> - Sets number of digits for floating point values
574 .  -glvis_size <width,height> - Sets the window size (in pixels)
575 .  -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization
576        (0 is default, -1 implies every visualization)
577 .  -glvis_keys - Additional keys to configure visualization
578 -  -glvis_exec - Additional commands to configure visualization
579 
580   Fortran Note:
581   Missing Fortran binding
582 
583   Level: beginner
584 
585 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType`
586 @*/
587 PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer) {
588   PetscViewerGLVis socket;
589 
590   PetscFunctionBegin;
591   PetscCall(PetscViewerCreate(comm, viewer));
592   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));
593 
594   socket       = (PetscViewerGLVis)((*viewer)->data);
595   socket->type = type;
596   if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
597     PetscCall(PetscFree(socket->name));
598     PetscCall(PetscStrallocpy(name, &socket->name));
599   }
600   socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;
601 
602   PetscCall(PetscViewerSetFromOptions(*viewer));
603   PetscFunctionReturn(0);
604 }
605 
606 /*
607   PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.
608 
609   Collective
610 
611   Input Parameter:
612 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`
613 
614   Level: intermediate
615 
616   Environmental variables:
617 + `PETSC_VIEWER_GLVIS_FILENAME` : output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`)
618 . `PETSC_VIEWER_GLVIS_HOSTNAME` : machine where the GLVis server is listening (defaults to localhost)
619 - `PETSC_VIEWER_GLVIS_PORT`     : port opened by the GLVis server (defaults to 19916)
620 
621   Note:
622   Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return
623   an error code.  It is usually used in the form
624 $       XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));
625 
626   Fortran Note:
627   Missing Fortran bindings
628 
629 .seealso: `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
630 */
631 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) {
632   PetscErrorCode       ierr;
633   PetscBool            flg;
634   PetscViewer          viewer;
635   PetscViewerGLVisType type;
636   char                 fname[PETSC_MAX_PATH_LEN], sport[16];
637   PetscInt             port = 19916; /* default for GLVis */
638 
639   PetscFunctionBegin;
640   ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg);
641   if (ierr) {
642     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
643     PetscFunctionReturn(NULL);
644   }
645   if (!flg) {
646     type = PETSC_VIEWER_GLVIS_SOCKET;
647     ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg);
648     if (ierr) {
649       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
650       PetscFunctionReturn(NULL);
651     }
652     if (!flg) {
653       ierr = PetscStrcpy(fname, "localhost");
654       if (ierr) {
655         PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
656         PetscFunctionReturn(NULL);
657       }
658     }
659     ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg);
660     if (ierr) {
661       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
662       PetscFunctionReturn(NULL);
663     }
664     if (flg) {
665       ierr = PetscOptionsStringToInt(sport, &port);
666       if (ierr) {
667         PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
668         PetscFunctionReturn(NULL);
669       }
670     }
671   } else {
672     type = PETSC_VIEWER_GLVIS_DUMP;
673   }
674   ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer);
675   if (ierr) {
676     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
677     PetscFunctionReturn(NULL);
678   }
679   ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
680   if (ierr) {
681     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
682     PetscFunctionReturn(NULL);
683   }
684   PetscFunctionReturn(viewer);
685 }
686 
687 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) {
688   PetscViewerGLVis socket;
689 
690   PetscFunctionBegin;
691   PetscCall(PetscNewLog(viewer, &socket));
692 
693   /* defaults to socket viewer */
694   PetscCall(PetscStrallocpy("localhost", &socket->name));
695   socket->port  = 19916; /* GLVis default listening port */
696   socket->type  = PETSC_VIEWER_GLVIS_SOCKET;
697   socket->pause = 0; /* just pause the first time */
698 
699   socket->windowsizes[0] = 600;
700   socket->windowsizes[1] = 600;
701 
702   /* defaults to full precision */
703   PetscCall(PetscStrallocpy(" %g", &socket->fmt));
704 
705   viewer->data                = (void *)socket;
706   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
707   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
708 
709   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis));
710   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis));
711   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis));
712   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis));
713   PetscFunctionReturn(0);
714 }
715 
716 /* this is a private implementation of a SOCKET with ASCII data format
717    GLVis does not currently handle binary socket streams */
718 #if defined(PETSC_HAVE_UNISTD_H)
719 #include <unistd.h>
720 #endif
721 
722 #if !defined(PETSC_HAVE_WINDOWS_H)
723 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
724 
725 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) {
726   FILE *stream;
727 
728   PetscFunctionBegin;
729   PetscCall(PetscViewerASCIIGetPointer(viewer, &stream));
730   if (stream) {
731     int retv = fclose(stream);
732     PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream");
733   }
734   PetscCall(PetscViewerDestroy_ASCII(viewer));
735   PetscFunctionReturn(0);
736 }
737 #endif
738 
739 /*
740     This attempts to return a NULL viewer if it is unable to open a socket connection.
741 
742      The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
743 
744      The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value
745      of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
746 */
747 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) {
748 #if defined(PETSC_HAVE_WINDOWS_H)
749   PetscFunctionBegin;
750   SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows");
751 #else
752   FILE          *stream = NULL;
753   int            fd     = 0;
754   PetscErrorCode ierr;
755 
756   PetscFunctionBegin;
757   PetscValidCharPointer(hostname, 2);
758   PetscValidPointer(viewer, 4);
759 #if defined(PETSC_USE_SOCKET_VIEWER)
760   ierr = 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];
771 
772     PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
773     PetscCall(PetscInfo(NULL, "%s", err));
774     *viewer = NULL;
775     PetscFunctionReturn(0);
776   } else {
777     char msg[1024];
778 
779     PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
780     PetscCall(PetscInfo(NULL, "%s", msg));
781   }
782   stream = fdopen(fd, "w"); /* Not possible on Windows */
783   PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
784   PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
785   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
786   (*viewer)->ops->destroy  = PetscViewerDestroy_ASCII_Socket;
787 #endif
788   PetscFunctionReturn(0);
789 }
790 
791 #if !defined(PETSC_MISSING_SIGPIPE)
792 
793 #include <signal.h>
794 
795 #if defined(PETSC_HAVE_WINDOWS_H)
796 #define PETSC_DEVNULL "NUL"
797 #else
798 #define PETSC_DEVNULL "/dev/null"
799 #endif
800 
801 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
802 
803 static void (*PetscGLVisSigHandler_save)(int) = NULL;
804 
805 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) {
806   PetscGLVisBrokenPipe = PETSC_TRUE;
807 #if !defined(PETSC_MISSING_SIG_IGN)
808   signal(SIGPIPE, SIG_IGN);
809 #endif
810 }
811 
812 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) {
813   PetscFunctionBegin;
814   PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
815   PetscGLVisBrokenPipe      = PETSC_FALSE;
816   PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
817   PetscFunctionReturn(0);
818 }
819 
820 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) {
821   PetscBool flag, brokenpipe;
822 
823   PetscFunctionBegin;
824   flag = PetscGLVisBrokenPipe;
825   PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
826   if (brokenpipe) {
827     FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
828     PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
829     PetscCall(PetscViewerASCIISetFILE(*win, null));
830     PetscCall(PetscViewerDestroy(win));
831     if (sock) (void)fclose(sock);
832   }
833   (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
834   PetscGLVisSigHandler_save = NULL;
835   PetscGLVisBrokenPipe      = PETSC_FALSE;
836   PetscFunctionReturn(0);
837 }
838 
839 #else
840 
841 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) {
842   PetscFunctionBegin;
843   PetscFunctionReturn(0);
844 }
845 
846 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) {
847   PetscFunctionBegin;
848   PetscFunctionReturn(0);
849 }
850 
851 #endif
852