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