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