xref: /petsc/src/sys/classes/viewer/impls/glvis/glvis.c (revision a16fd2c93c02146fccd68469496ac02ca99b9ebe)
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   PetscErrorCode   ierr;
207 
208   PetscFunctionBegin;
209   ierr = PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window);
210   /* if we could not estabilish a connection the first time, we disable the socket viewer */
211   ldis = ierr ? 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 GLVis type viewer
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 disk
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   Notes:
581     misses Fortran binding
582 
583   Level: beginner
584 
585 .seealso: `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 an GLVIS PetscViewer shared by all processors in a communicator.
608 
609   Collective
610 
611   Input Parameter:
612 . comm - the MPI communicator to share the GLVIS PetscViewer
613 
614   Level: intermediate
615 
616   Notes:
617     misses Fortran bindings
618 
619   Environmental variables:
620 + PETSC_VIEWER_GLVIS_FILENAME : output filename (if specified dump to disk, and takes precedence on PETSC_VIEWER_GLVIS_HOSTNAME)
621 . PETSC_VIEWER_GLVIS_HOSTNAME : machine where the GLVis server is listening (defaults to localhost)
622 - PETSC_VIEWER_GLVIS_PORT     : port opened by the GLVis server (defaults to 19916)
623 
624   Notes:
625   Unlike almost all other PETSc routines, PETSC_VIEWER_GLVIS_ does not return
626   an error code.  The GLVIS PetscViewer is usually used in the form
627 $       XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));
628 
629 .seealso: `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 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) {
740 #if defined(PETSC_HAVE_WINDOWS_H)
741   PetscFunctionBegin;
742   SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows");
743 #else
744   FILE          *stream = NULL;
745   int            fd     = 0;
746   PetscErrorCode ierr;
747 
748   PetscFunctionBegin;
749   PetscValidCharPointer(hostname, 2);
750   PetscValidPointer(viewer, 4);
751 #if defined(PETSC_USE_SOCKET_VIEWER)
752   ierr = PetscOpenSocket(hostname, port, &fd);
753 #else
754   SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
755 #endif
756   if (PetscUnlikely(ierr)) {
757     PetscInt sierr = ierr;
758     char     err[1024];
759 
760     PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
761     PetscCall(PetscInfo(NULL, "%s", err));
762     *viewer = NULL;
763     PetscFunctionReturn(sierr);
764   } else {
765     char msg[1024];
766 
767     PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
768     PetscCall(PetscInfo(NULL, "%s", msg));
769   }
770   stream = fdopen(fd, "w"); /* Not possible on Windows */
771   PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
772   PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
773   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
774   (*viewer)->ops->destroy  = PetscViewerDestroy_ASCII_Socket;
775 #endif
776   PetscFunctionReturn(0);
777 }
778 
779 #if !defined(PETSC_MISSING_SIGPIPE)
780 
781 #include <signal.h>
782 
783 #if defined(PETSC_HAVE_WINDOWS_H)
784 #define PETSC_DEVNULL "NUL"
785 #else
786 #define PETSC_DEVNULL "/dev/null"
787 #endif
788 
789 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
790 
791 static void (*PetscGLVisSigHandler_save)(int) = NULL;
792 
793 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) {
794   PetscGLVisBrokenPipe = PETSC_TRUE;
795 #if !defined(PETSC_MISSING_SIG_IGN)
796   signal(SIGPIPE, SIG_IGN);
797 #endif
798 }
799 
800 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) {
801   PetscFunctionBegin;
802   PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
803   PetscGLVisBrokenPipe      = PETSC_FALSE;
804   PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
805   PetscFunctionReturn(0);
806 }
807 
808 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) {
809   PetscBool flag, brokenpipe;
810 
811   PetscFunctionBegin;
812   flag = PetscGLVisBrokenPipe;
813   PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
814   if (brokenpipe) {
815     FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
816     PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
817     PetscCall(PetscViewerASCIISetFILE(*win, null));
818     PetscCall(PetscViewerDestroy(win));
819     if (sock) (void)fclose(sock);
820   }
821   (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
822   PetscGLVisSigHandler_save = NULL;
823   PetscGLVisBrokenPipe      = PETSC_FALSE;
824   PetscFunctionReturn(0);
825 }
826 
827 #else
828 
829 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) {
830   PetscFunctionBegin;
831   PetscFunctionReturn(0);
832 }
833 
834 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) {
835   PetscFunctionBegin;
836   PetscFunctionReturn(0);
837 }
838 
839 #endif
840