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