xref: /petsc/src/sys/classes/viewer/impls/glvis/glvis.c (revision 3307d110e72ee4e6d2468971620073eb5ff93529)
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   PetscCheckFalse(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   PetscCheckFalse(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) {
287       PetscCall(PetscViewerPushFormat(socket->meshwindow,PETSC_VIEWER_ASCII_GLVIS));
288     }
289   }
290   if (socket->meshwindow) {
291     PetscCall(PetscViewerGLVisAttachInfo_Private(viewer,socket->meshwindow));
292   }
293   *view = socket->meshwindow;
294   PetscFunctionReturn(0);
295 }
296 
297 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer,PetscViewer *view)
298 {
299   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
300 
301   PetscFunctionBegin;
302   PetscValidPointer(view,2);
303   PetscCheckFalse(*view && *view != socket->meshwindow,PetscObjectComm((PetscObject)viewer),PETSC_ERR_USER,"Viewer was not obtained from PetscViewerGLVisGetDMWindow()");
304   if (*view) {
305     PetscCall(PetscViewerFlush(*view));
306     PetscCall(PetscBarrier((PetscObject)viewer));
307   }
308   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
309     PetscCall(PetscViewerDestroy(&socket->meshwindow));
310   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
311     socket->meshwindow = NULL;
312   }
313   *view = NULL;
314   PetscFunctionReturn(0);
315 }
316 
317 PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer,PetscViewerGLVisType *type)
318 {
319   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
320 
321   PetscFunctionBegin;
322   PetscValidPointer(type,2);
323   *type = socket->type;
324   PetscFunctionReturn(0);
325 }
326 
327 /* 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 */
328 PetscErrorCode PetscViewerGLVisGetStatus_Private(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
329 {
330   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
331 
332   PetscFunctionBegin;
333   PetscValidPointer(sockstatus,2);
334   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
335     socket->status = PETSCVIEWERGLVIS_DISCONNECTED;
336   } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) {
337     PetscInt       i;
338     PetscBool      lconn,conn;
339 
340     for (i=0,lconn=PETSC_TRUE;i<socket->nwindow;i++)
341       if (!socket->window[i])
342         lconn = PETSC_FALSE;
343 
344     PetscCall(MPIU_Allreduce(&lconn,&conn,1,MPIU_BOOL,MPI_LAND,PetscObjectComm((PetscObject)viewer)));
345     if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
346   }
347   *sockstatus = socket->status;
348   PetscFunctionReturn(0);
349 }
350 
351 PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject* dm)
352 {
353   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
354 
355   PetscFunctionBegin;
356   *dm = socket->dm;
357   PetscFunctionReturn(0);
358 }
359 
360 PetscErrorCode PetscViewerGLVisGetFields_Private(PetscViewer viewer, PetscInt* nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode(**g2lfield)(PetscObject,PetscInt,PetscObject[],void*), PetscObject *Ufield[], void **userctx)
361 {
362   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
363 
364   PetscFunctionBegin;
365   if (nfield)   *nfield   = socket->nwindow;
366   if (fec)      *fec      = (const char**)socket->fec_type;
367   if (spacedim) *spacedim = socket->spacedim;
368   if (g2lfield) *g2lfield = socket->g2lfield;
369   if (Ufield)   *Ufield   = socket->Ufield;
370   if (userctx)  *userctx  = socket->userctx;
371   PetscFunctionReturn(0);
372 }
373 
374 /* accessor routines for the viewer windows:
375    PETSC_VIEWER_GLVIS_DUMP   : it returns a new viewer every time
376    PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done.
377 */
378 PetscErrorCode PetscViewerGLVisGetWindow_Private(PetscViewer viewer,PetscInt wid,PetscViewer* view)
379 {
380   PetscViewerGLVis       socket = (PetscViewerGLVis)viewer->data;
381   PetscViewerGLVisStatus status;
382 
383   PetscFunctionBegin;
384   PetscValidLogicalCollectiveInt(viewer,wid,2);
385   PetscValidPointer(view,3);
386   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);
387   status = socket->status;
388   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);
389   switch (status) {
390     case PETSCVIEWERGLVIS_DISCONNECTED:
391       PetscCheck(!socket->window[wid],PETSC_COMM_SELF,PETSC_ERR_USER,"This should not happen");
392       else if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
393         size_t    len;
394         PetscBool isstdout;
395 
396         PetscCall(PetscStrlen(socket->name,&len));
397         PetscCall(PetscStrcmp(socket->name,"stdout",&isstdout));
398         if (!socket->name || !len || isstdout) {
399           PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF,"stdout",&socket->window[wid]));
400         } else {
401           PetscMPIInt rank;
402           char        filename[PETSC_MAX_PATH_LEN];
403 
404           PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer),&rank));
405           PetscCall(PetscSNPrintf(filename,PETSC_MAX_PATH_LEN,"%s-%s-%" PetscInt_FMT ".%06d",socket->name,socket->windowtitle[wid],socket->snapid,rank));
406           PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF,filename,&socket->window[wid]));
407         }
408       } else {
409         PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer,&socket->window[wid]));
410       }
411       if (socket->window[wid]) {
412         PetscCall(PetscViewerPushFormat(socket->window[wid],PETSC_VIEWER_ASCII_GLVIS));
413       }
414       *view = socket->window[wid];
415       break;
416     case PETSCVIEWERGLVIS_CONNECTED:
417       *view = socket->window[wid];
418       break;
419     case PETSCVIEWERGLVIS_DISABLED:
420       *view = NULL;
421       break;
422     default:
423       SETERRQ(PetscObjectComm((PetscObject)viewer),PETSC_ERR_SUP,"Unhandled socket status %d",(int)status);
424   }
425   if (*view) {
426     PetscCall(PetscViewerGLVisAttachInfo_Private(viewer,*view));
427   }
428   PetscFunctionReturn(0);
429 }
430 
431 /* Restore the window viewer
432    PETSC_VIEWER_GLVIS_DUMP  : destroys the temporary created ASCII viewer used for dumping
433    PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer.
434                  - it the returned window viewer is NULL, assumes something went wrong
435                    with the socket (i.e. SIGPIPE when a user closes the popup window)
436                    and that the caller already handled it (see VecView_GLVis).
437 */
438 PetscErrorCode PetscViewerGLVisRestoreWindow_Private(PetscViewer viewer,PetscInt wid, PetscViewer* view)
439 {
440   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
441 
442   PetscFunctionBegin;
443   PetscValidHeaderSpecificType(viewer,PETSC_VIEWER_CLASSID,1,PETSCVIEWERGLVIS);
444   PetscValidLogicalCollectiveInt(viewer,wid,2);
445   PetscValidPointer(view,3);
446   PetscCheckFalse(wid < 0 || wid > socket->nwindow-1,PetscObjectComm((PetscObject)viewer),PETSC_ERR_USER,"Cannot restore window id %" PetscInt_FMT ": allowed range [0,%" PetscInt_FMT ")",wid,socket->nwindow);
447   PetscCheckFalse(*view && *view != socket->window[wid],PetscObjectComm((PetscObject)viewer),PETSC_ERR_USER,"Viewer was not obtained from PetscViewerGLVisGetWindow()");
448   if (*view) {
449     PetscCall(PetscViewerFlush(*view));
450     PetscCall(PetscBarrier((PetscObject)viewer));
451   }
452   if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
453     PetscCall(PetscViewerDestroy(&socket->window[wid]));
454   } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
455     socket->window[wid] = NULL;
456   }
457   *view = NULL;
458   PetscFunctionReturn(0);
459 }
460 
461 /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
462 PetscErrorCode PetscViewerGLVisInitWindow_Private(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
463 {
464   PetscViewerGLVisInfo info;
465   PetscContainer       container;
466 
467   PetscFunctionBegin;
468   PetscCall(PetscObjectQuery((PetscObject)viewer,"_glvis_info_container",(PetscObject*)&container));
469   PetscCheck(container,PETSC_COMM_SELF,PETSC_ERR_USER,"Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private");
470   PetscCall(PetscContainerGetPointer(container,(void**)&info));
471   if (info->init) PetscFunctionReturn(0);
472 
473   /* Configure window */
474   if (info->size[0] > 0) {
475     PetscCall(PetscViewerASCIIPrintf(viewer,"window_size %" PetscInt_FMT " %" PetscInt_FMT "\n",info->size[0],info->size[1]));
476   }
477   if (name) {
478     PetscCall(PetscViewerASCIIPrintf(viewer,"window_title '%s'\n",name));
479   }
480 
481   /* Configure default view */
482   if (mesh) {
483     switch (dim) {
484     case 1:
485       PetscCall(PetscViewerASCIIPrintf(viewer,"keys m\n")); /* show mesh */
486       break;
487     case 2:
488       PetscCall(PetscViewerASCIIPrintf(viewer,"keys m\n")); /* show mesh */
489       break;
490     case 3: /* TODO: decide default view in 3D */
491       break;
492     }
493   } else {
494     PetscCall(PetscViewerASCIIPrintf(viewer,"keys cm\n")); /* show colorbar and mesh */
495     switch (dim) {
496     case 1:
497       PetscCall(PetscViewerASCIIPrintf(viewer,"keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */
498       break;
499     case 2:
500       PetscCall(PetscViewerASCIIPrintf(viewer,"keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */
501       break;
502     case 3:
503       break;
504     }
505     PetscCall(PetscViewerASCIIPrintf(viewer,"autoscale value\n")); /* update value-range; keep mesh-extents fixed */
506   }
507 
508   { /* Additional keys and commands */
509     char keys[256] = "", cmds[2*PETSC_MAX_PATH_LEN] = "";
510     PetscOptions opt = ((PetscObject)viewer)->options;
511     const char  *pre = ((PetscObject)viewer)->prefix;
512 
513     PetscCall(PetscOptionsGetString(opt,pre,"-glvis_keys",keys,sizeof(keys),NULL));
514     PetscCall(PetscOptionsGetString(opt,pre,"-glvis_exec",cmds,sizeof(cmds),NULL));
515     if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer,"keys %s\n",keys));
516     if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer,"%s\n",cmds));
517   }
518 
519   /* Pause visualization */
520   if (!mesh && info->pause == -1) {
521     PetscCall(PetscViewerASCIIPrintf(viewer,"autopause 1\n"));
522   }
523   if (!mesh && info->pause == 0) {
524     PetscCall(PetscViewerASCIIPrintf(viewer,"pause\n"));
525   }
526 
527   info->init = PETSC_TRUE;
528   PetscFunctionReturn(0);
529 }
530 
531 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
532 {
533   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
534   PetscInt         i;
535 
536   PetscFunctionBegin;
537   for (i=0;i<socket->nwindow;i++) {
538     PetscCall(PetscViewerDestroy(&socket->window[i]));
539     PetscCall(PetscFree(socket->windowtitle[i]));
540     PetscCall(PetscFree(socket->fec_type[i]));
541     PetscCall(PetscObjectDestroy(&socket->Ufield[i]));
542   }
543   PetscCall(PetscFree(socket->name));
544   PetscCall(PetscFree5(socket->window,socket->windowtitle,socket->fec_type,socket->spacedim,socket->Ufield));
545   PetscCall(PetscFree(socket->fmt));
546   PetscCall(PetscViewerDestroy(&socket->meshwindow));
547   PetscCall(PetscObjectDestroy(&socket->dm));
548   if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));
549 
550   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerGLVisSetPrecision_C",NULL));
551   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerGLVisSetSnapId_C",NULL));
552   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerGLVisSetFields_C",NULL));
553   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerFileSetName_C",NULL));
554   PetscCall(PetscFree(socket));
555   viewer->data = NULL;
556   PetscFunctionReturn(0);
557 }
558 
559 static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscOptionItems *PetscOptionsObject,PetscViewer v)
560 {
561   PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
562   PetscInt         nsizes = 2, prec = PETSC_DECIDE;
563   PetscBool        set;
564 
565   PetscFunctionBegin;
566   PetscOptionsHeadBegin(PetscOptionsObject,"GLVis PetscViewer Options");
567   PetscCall(PetscOptionsInt("-glvis_precision","Number of digits for floating point values","PetscViewerGLVisSetPrecision",prec,&prec,&set));
568   if (set) PetscCall(PetscViewerGLVisSetPrecision(v,prec));
569   PetscCall(PetscOptionsIntArray("-glvis_size","Window sizes",NULL,socket->windowsizes,&nsizes,&set));
570   if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0];
571   PetscCall(PetscOptionsReal("-glvis_pause","-1 to pause after each visualization, otherwise sleeps for given seconds",NULL,socket->pause,&socket->pause,NULL));
572   PetscCall(PetscOptionsName("-glvis_keys","Additional keys to configure visualization",NULL,NULL));
573   PetscCall(PetscOptionsName("-glvis_exec","Additional commands to configure visualization",NULL,NULL));
574   PetscOptionsHeadEnd();
575   PetscFunctionReturn(0);
576 }
577 
578 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
579 {
580   char             *sport;
581   PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
582 
583   PetscFunctionBegin;
584   socket->type = PETSC_VIEWER_GLVIS_DUMP;
585   /* we accept localhost^port */
586   PetscCall(PetscFree(socket->name));
587   PetscCall(PetscStrallocpy(name,&socket->name));
588   PetscCall(PetscStrchr(socket->name,'^',&sport));
589   if (sport) {
590     PetscInt       port = 19916;
591     size_t         len;
592     PetscErrorCode ierr;
593 
594     *sport++ = 0;
595     PetscCall(PetscStrlen(sport,&len));
596     ierr = PetscOptionsStringToInt(sport,&port);
597     if (PetscUnlikely(ierr)) {
598       socket->port = 19916;
599     } else {
600       socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916;
601     }
602     socket->type = PETSC_VIEWER_GLVIS_SOCKET;
603   }
604   PetscFunctionReturn(0);
605 }
606 
607 /*@C
608   PetscViewerGLVisOpen - Opens a GLVis type viewer
609 
610   Collective
611 
612   Input Parameters:
613 +  comm      - the MPI communicator
614 .  type      - the viewer type: PETSC_VIEWER_GLVIS_SOCKET for real-time visualization or PETSC_VIEWER_GLVIS_DUMP for dumping to disk
615 .  name      - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations
616 -  port      - socket port where the GLVis server is listening. Not referenced when type is PETSC_VIEWER_GLVIS_DUMP
617 
618   Output Parameters:
619 -  viewer    - the PetscViewer object
620 
621   Options Database Keys:
622 +  -glvis_precision <precision> - Sets number of digits for floating point values
623 .  -glvis_size <width,height> - Sets the window size (in pixels)
624 .  -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization
625        (0 is default, -1 implies every visualization)
626 .  -glvis_keys - Additional keys to configure visualization
627 -  -glvis_exec - Additional commands to configure visualization
628 
629   Notes:
630     misses Fortran binding
631 
632   Level: beginner
633 
634 .seealso: PetscViewerCreate(), PetscViewerSetType(), PetscViewerGLVisType
635 @*/
636 PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer)
637 {
638   PetscViewerGLVis socket;
639 
640   PetscFunctionBegin;
641   PetscCall(PetscViewerCreate(comm,viewer));
642   PetscCall(PetscViewerSetType(*viewer,PETSCVIEWERGLVIS));
643 
644   socket       = (PetscViewerGLVis)((*viewer)->data);
645   socket->type = type;
646   if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
647     PetscCall(PetscFree(socket->name));
648     PetscCall(PetscStrallocpy(name,&socket->name));
649   }
650   socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;
651 
652   PetscCall(PetscViewerSetFromOptions(*viewer));
653   PetscFunctionReturn(0);
654 }
655 
656 /*
657   PETSC_VIEWER_GLVIS_ - Creates an GLVIS PetscViewer shared by all processors in a communicator.
658 
659   Collective
660 
661   Input Parameter:
662 . comm - the MPI communicator to share the GLVIS PetscViewer
663 
664   Level: intermediate
665 
666   Notes:
667     misses Fortran bindings
668 
669   Environmental variables:
670 + PETSC_VIEWER_GLVIS_FILENAME : output filename (if specified dump to disk, and takes precedence on PETSC_VIEWER_GLVIS_HOSTNAME)
671 . PETSC_VIEWER_GLVIS_HOSTNAME : machine where the GLVis server is listening (defaults to localhost)
672 - PETSC_VIEWER_GLVIS_PORT     : port opened by the GLVis server (defaults to 19916)
673 
674   Notes:
675   Unlike almost all other PETSc routines, PETSC_VIEWER_GLVIS_ does not return
676   an error code.  The GLVIS PetscViewer is usually used in the form
677 $       XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));
678 
679 .seealso: PetscViewerGLVISOpen(), PetscViewerGLVisType, PetscViewerCreate(), PetscViewerDestroy()
680 */
681 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm)
682 {
683   PetscErrorCode       ierr;
684   PetscBool            flg;
685   PetscViewer          viewer;
686   PetscViewerGLVisType type;
687   char                 fname[PETSC_MAX_PATH_LEN],sport[16];
688   PetscInt             port = 19916; /* default for GLVis */
689 
690   PetscFunctionBegin;
691   ierr = PetscOptionsGetenv(comm,"PETSC_VIEWER_GLVIS_FILENAME",fname,PETSC_MAX_PATH_LEN,&flg);
692   if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_VIEWER_GLVIS_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
693   if (!flg) {
694     type = PETSC_VIEWER_GLVIS_SOCKET;
695     ierr = PetscOptionsGetenv(comm,"PETSC_VIEWER_GLVIS_HOSTNAME",fname,PETSC_MAX_PATH_LEN,&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 = PetscStrcpy(fname,"localhost");
699       if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_VIEWER_GLVIS_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
700     }
701     ierr = PetscOptionsGetenv(comm,"PETSC_VIEWER_GLVIS_PORT",sport,16,&flg);
702     if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_VIEWER_GLVIS_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
703     if (flg) {
704       ierr = PetscOptionsStringToInt(sport,&port);
705       if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_VIEWER_GLVIS_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
706     }
707   } else {
708     type = PETSC_VIEWER_GLVIS_DUMP;
709   }
710   ierr = PetscViewerGLVisOpen(comm,type,fname,port,&viewer);
711   if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_VIEWER_GLVIS_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
712   ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
713   if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_VIEWER_GLVIS_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
714   PetscFunctionReturn(viewer);
715 }
716 
717 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
718 {
719   PetscViewerGLVis socket;
720 
721   PetscFunctionBegin;
722   PetscCall(PetscNewLog(viewer,&socket));
723 
724   /* defaults to socket viewer */
725   PetscCall(PetscStrallocpy("localhost",&socket->name));
726   socket->port  = 19916; /* GLVis default listening port */
727   socket->type  = PETSC_VIEWER_GLVIS_SOCKET;
728   socket->pause = 0; /* just pause the first time */
729 
730   socket->windowsizes[0] = 600;
731   socket->windowsizes[1] = 600;
732 
733   /* defaults to full precision */
734   PetscCall(PetscStrallocpy(" %g",&socket->fmt));
735 
736   viewer->data                = (void*)socket;
737   viewer->ops->destroy        = PetscViewerDestroy_GLVis;
738   viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
739 
740   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerGLVisSetPrecision_C",PetscViewerGLVisSetPrecision_GLVis));
741   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerGLVisSetSnapId_C",PetscViewerGLVisSetSnapId_GLVis));
742   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerGLVisSetFields_C",PetscViewerGLVisSetFields_GLVis));
743   PetscCall(PetscObjectComposeFunction((PetscObject)viewer,"PetscViewerFileSetName_C",PetscViewerFileSetName_GLVis));
744   PetscFunctionReturn(0);
745 }
746 
747 /* this is a private implementation of a SOCKET with ASCII data format
748    GLVis does not currently handle binary socket streams */
749 #if defined(PETSC_HAVE_UNISTD_H)
750 #include <unistd.h>
751 #endif
752 
753 #if !defined(PETSC_HAVE_WINDOWS_H)
754 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
755 
756 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
757 {
758   FILE *stream;
759   int  err = 0;
760 
761   PetscFunctionBegin;
762   PetscCall(PetscViewerASCIIGetPointer(viewer,&stream));
763   if (stream) {
764     err = fclose(stream);
765     PetscCheck(err,PETSC_COMM_SELF,PETSC_ERR_SYS,"fclose() failed on stream");
766   }
767   PetscCall(PetscViewerDestroy_ASCII(viewer));
768   PetscFunctionReturn(0);
769 }
770 #endif
771 
772 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm,const char* hostname,PetscInt port,PetscViewer* viewer)
773 {
774 #if defined(PETSC_HAVE_WINDOWS_H)
775   PetscFunctionBegin;
776   SETERRQ(comm,PETSC_ERR_SUP,"Not implemented for Windows");
777 #else
778   FILE           *stream = NULL;
779   int            fd=0;
780   PetscErrorCode ierr;
781 
782   PetscFunctionBegin;
783   PetscValidCharPointer(hostname,2);
784   PetscValidPointer(viewer,4);
785 #if defined(PETSC_USE_SOCKET_VIEWER)
786   ierr = PetscOpenSocket(hostname,port,&fd);
787 #else
788   SETERRQ(comm,PETSC_ERR_SUP,"Missing Socket viewer");
789 #endif
790   if (PetscUnlikely(ierr)) {
791     PetscInt sierr = ierr;
792     char     err[1024];
793 
794     PetscCall(PetscSNPrintf(err,1024,"Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n",hostname,port));
795     PetscCall(PetscInfo(NULL,"%s",err));
796     *viewer = NULL;
797     PetscFunctionReturn(sierr);
798   } else {
799     char msg[1024];
800 
801     PetscCall(PetscSNPrintf(msg,1024,"Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n",hostname,port));
802     PetscCall(PetscInfo(NULL,"%s",msg));
803   }
804   stream = fdopen(fd,"w"); /* Not possible on Windows */
805   PetscCheck(stream,PETSC_COMM_SELF,PETSC_ERR_SYS,"Cannot open stream from socket %s:%" PetscInt_FMT,hostname,port);
806   PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF,stream,viewer));
807   PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
808   (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket;
809 #endif
810   PetscFunctionReturn(0);
811 }
812 
813 #if !defined(PETSC_MISSING_SIGPIPE)
814 
815 #include <signal.h>
816 
817 #if defined(PETSC_HAVE_WINDOWS_H)
818 #define PETSC_DEVNULL "NUL"
819 #else
820 #define PETSC_DEVNULL "/dev/null"
821 #endif
822 
823 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
824 
825 static void (*PetscGLVisSigHandler_save)(int) = NULL;
826 
827 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
828 {
829   PetscGLVisBrokenPipe = PETSC_TRUE;
830 #if !defined(PETSC_MISSING_SIG_IGN)
831   signal(SIGPIPE,SIG_IGN);
832 #endif
833 }
834 
835 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm,PETSC_UNUSED PetscViewer *win)
836 {
837   PetscFunctionBegin;
838   PetscCheck(!PetscGLVisSigHandler_save,comm,PETSC_ERR_PLIB,"Nested call to %s()",PETSC_FUNCTION_NAME);
839   PetscGLVisBrokenPipe = PETSC_FALSE;
840   PetscGLVisSigHandler_save = signal(SIGPIPE,PetscGLVisSigHandler_SIGPIPE);
841   PetscFunctionReturn(0);
842 }
843 
844 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm,PetscViewer *win)
845 {
846   PetscBool      flag,brokenpipe;
847 
848   PetscFunctionBegin;
849   flag = PetscGLVisBrokenPipe;
850   PetscCall(MPIU_Allreduce(&flag,&brokenpipe,1,MPIU_BOOL,MPI_LOR,comm));
851   if (brokenpipe) {
852     FILE *sock, *null = fopen(PETSC_DEVNULL,"w");
853     PetscCall(PetscViewerASCIIGetPointer(*win,&sock));
854     PetscCall(PetscViewerASCIISetFILE(*win,null));
855     PetscCall(PetscViewerDestroy(win));
856     if (sock) (void)fclose(sock);
857   }
858   (void)signal(SIGPIPE,PetscGLVisSigHandler_save);
859   PetscGLVisSigHandler_save = NULL;
860   PetscGLVisBrokenPipe = PETSC_FALSE;
861   PetscFunctionReturn(0);
862 }
863 
864 #else
865 
866 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm,PETSC_UNUSED PetscViewer *win)
867 {
868   PetscFunctionBegin;
869   PetscFunctionReturn(0);
870 }
871 
872 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm,PETSC_UNUSED PetscViewer *win)
873 {
874   PetscFunctionBegin;
875   PetscFunctionReturn(0);
876 }
877 
878 #endif
879