Actual source code: glvis.c
1: #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fdopen() */
3: #include <petsc/private/viewerimpl.h>
4: #include <petsc/private/petscimpl.h>
5: #include <petsc/private/glvisviewerimpl.h>
7: /* we may eventually make this function public */
8: static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm, const char *, PetscInt, PetscViewer *);
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;
33: /*@
34: PetscViewerGLVisSetPrecision - Set the number of digits for floating point values to be displayed
36: Not Collective
38: Input Parameters:
39: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
40: - prec - the number of digits required
42: Level: beginner
44: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
45: @*/
46: PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec)
47: {
48: PetscFunctionBegin;
50: PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec));
51: PetscFunctionReturn(PETSC_SUCCESS);
52: }
54: static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec)
55: {
56: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
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(PETSC_SUCCESS);
67: }
69: /*@
70: PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP`
72: Logically Collective
74: Input Parameters:
75: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS`
76: - id - the current snapshot id in a time-dependent simulation
78: Level: beginner
80: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()`
81: @*/
82: PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id)
83: {
84: PetscFunctionBegin;
87: PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id));
88: PetscFunctionReturn(PETSC_SUCCESS);
89: }
91: static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id)
92: {
93: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
95: PetscFunctionBegin;
96: socket->snapid = id;
97: PetscFunctionReturn(PETSC_SUCCESS);
98: }
100: /*@C
101: PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector.
103: Logically Collective
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: . g2l - 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
115: Level: intermediate
117: Notes:
118: `g2lfields` is called on the vector V to be visualized in order to extract the relevant dofs to be put in `Vfield`, as
119: .vb
120: g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx).
121: .ve
123: For vector spaces, the block size of `Vfield`[i] represents the vector dimension.
124: The names of the `Vfield` vectors will be displayed in the window title.
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;
133: PetscCheck(fec_type, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "You need to provide the FiniteElementCollection names for the fields");
134: PetscAssertPointer(fec_type, 3);
135: PetscAssertPointer(dim, 4);
136: PetscAssertPointer(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(PETSC_SUCCESS);
139: }
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;
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;
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;
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(PETSC_SUCCESS);
170: }
172: static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr)
173: {
174: PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr;
176: PetscFunctionBegin;
177: PetscCall(PetscFree(info->fmt));
178: PetscCall(PetscFree(info));
179: PetscFunctionReturn(PETSC_SUCCESS);
180: }
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;
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(PetscObjectContainerCompose((PetscObject)window, "_glvis_info_container", info, PetscViewerGLVisInfoDestroy_Private));
199: } else {
200: PetscCall(PetscContainerGetPointer(container, (void **)&info));
201: }
202: PetscCall(PetscFree(info->fmt));
203: PetscCall(PetscStrallocpy(socket->fmt, &info->fmt));
204: PetscFunctionReturn(PETSC_SUCCESS);
205: }
207: static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view)
208: {
209: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
210: PetscViewer window = NULL;
211: PetscBool ldis, dis;
213: PetscFunctionBegin;
214: PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window));
215: /* if we could not establish a connection, we disable the socket viewer on all MPI ranks */
216: ldis = !viewer ? PETSC_TRUE : PETSC_FALSE;
217: PetscCallMPI(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer)));
218: if (dis) {
219: socket->status = PETSCVIEWERGLVIS_DISABLED;
220: PetscCall(PetscViewerDestroy(&window));
221: }
222: *view = window;
223: PetscFunctionReturn(PETSC_SUCCESS);
224: }
226: PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer)
227: {
228: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
230: PetscFunctionBegin;
231: if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause));
232: PetscFunctionReturn(PETSC_SUCCESS);
233: }
235: /* DM specific support */
236: PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm)
237: {
238: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
240: PetscFunctionBegin;
241: PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer");
242: if (!socket->dm) {
243: PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL;
245: PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm));
246: if (setupwithdm) {
247: PetscCall((*setupwithdm)(dm, viewer));
248: } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
249: PetscCall(PetscObjectReference(dm));
250: socket->dm = dm;
251: }
252: PetscFunctionReturn(PETSC_SUCCESS);
253: }
255: PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
256: {
257: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
259: PetscFunctionBegin;
260: PetscAssertPointer(view, 2);
261: if (!socket->meshwindow) {
262: if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) {
263: PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow));
264: } else {
265: size_t len;
266: PetscBool isstdout;
268: PetscCall(PetscStrlen(socket->name, &len));
269: PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
270: if (!socket->name || !len || isstdout) {
271: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow));
272: } else {
273: PetscMPIInt rank;
274: char filename[PETSC_MAX_PATH_LEN];
275: PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
276: PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank));
277: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow));
278: }
279: }
280: if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS));
281: }
282: if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow));
283: *view = socket->meshwindow;
284: PetscFunctionReturn(PETSC_SUCCESS);
285: }
287: PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
288: {
289: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
291: PetscFunctionBegin;
292: PetscAssertPointer(view, 2);
293: PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()");
294: if (*view) {
295: PetscCall(PetscViewerFlush(*view));
296: PetscCall(PetscBarrier((PetscObject)viewer));
297: }
298: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
299: PetscCall(PetscViewerDestroy(&socket->meshwindow));
300: } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
301: socket->meshwindow = NULL;
302: }
303: *view = NULL;
304: PetscFunctionReturn(PETSC_SUCCESS);
305: }
307: PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
308: {
309: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
311: PetscFunctionBegin;
312: PetscAssertPointer(type, 2);
313: *type = socket->type;
314: PetscFunctionReturn(PETSC_SUCCESS);
315: }
317: /* 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 */
318: PetscErrorCode PetscViewerGLVisGetStatus_Internal(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
319: {
320: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
322: PetscFunctionBegin;
323: PetscAssertPointer(sockstatus, 2);
324: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
325: socket->status = PETSCVIEWERGLVIS_DISCONNECTED;
326: } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) {
327: PetscInt i;
328: PetscBool lconn, conn;
330: for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
331: if (!socket->window[i]) lconn = PETSC_FALSE;
333: PetscCallMPI(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer)));
334: if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
335: }
336: *sockstatus = socket->status;
337: PetscFunctionReturn(PETSC_SUCCESS);
338: }
340: PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
341: {
342: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
344: PetscFunctionBegin;
345: *dm = socket->dm;
346: PetscFunctionReturn(PETSC_SUCCESS);
347: }
349: PetscErrorCode PetscViewerGLVisGetFields_Internal(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx)
350: {
351: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
353: PetscFunctionBegin;
354: if (nfield) *nfield = socket->nwindow;
355: if (fec) *fec = (const char **)socket->fec_type;
356: if (spacedim) *spacedim = socket->spacedim;
357: if (g2lfield) *g2lfield = socket->g2lfield;
358: if (Ufield) *Ufield = socket->Ufield;
359: if (userctx) *userctx = socket->userctx;
360: PetscFunctionReturn(PETSC_SUCCESS);
361: }
363: /* accessor routines for the viewer windows:
364: PETSC_VIEWER_GLVIS_DUMP : it returns a new viewer every time
365: PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done.
366: */
367: PetscErrorCode PetscViewerGLVisGetWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
368: {
369: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
370: PetscViewerGLVisStatus status;
372: PetscFunctionBegin;
374: PetscAssertPointer(view, 3);
375: 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);
376: status = socket->status;
377: 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);
378: switch (status) {
379: case PETSCVIEWERGLVIS_DISCONNECTED:
380: PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen");
381: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
382: size_t len;
383: PetscBool isstdout;
385: PetscCall(PetscStrlen(socket->name, &len));
386: PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
387: if (!socket->name || !len || isstdout) {
388: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid]));
389: } else {
390: PetscMPIInt rank;
391: char filename[PETSC_MAX_PATH_LEN];
393: PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
394: PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank));
395: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid]));
396: }
397: } else {
398: PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid]));
399: }
400: if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS));
401: *view = socket->window[wid];
402: break;
403: case PETSCVIEWERGLVIS_CONNECTED:
404: *view = socket->window[wid];
405: break;
406: case PETSCVIEWERGLVIS_DISABLED:
407: *view = NULL;
408: break;
409: default:
410: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status);
411: }
412: if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view));
413: PetscFunctionReturn(PETSC_SUCCESS);
414: }
416: /* Restore the window viewer
417: PETSC_VIEWER_GLVIS_DUMP : destroys the temporary created ASCII viewer used for dumping
418: PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer.
419: - it the returned window viewer is NULL, assumes something went wrong
420: with the socket (i.e. SIGPIPE when a user closes the popup window)
421: and that the caller already handled it (see VecView_GLVis).
422: */
423: PetscErrorCode PetscViewerGLVisRestoreWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
424: {
425: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
427: PetscFunctionBegin;
430: PetscAssertPointer(view, 3);
431: 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);
432: PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()");
433: if (*view) {
434: PetscCall(PetscViewerFlush(*view));
435: PetscCall(PetscBarrier((PetscObject)viewer));
436: }
437: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
438: PetscCall(PetscViewerDestroy(&socket->window[wid]));
439: } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
440: socket->window[wid] = NULL;
441: }
442: *view = NULL;
443: PetscFunctionReturn(PETSC_SUCCESS);
444: }
446: /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
447: PetscErrorCode PetscViewerGLVisInitWindow_Internal(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
448: {
449: PetscViewerGLVisInfo info;
450: PetscContainer container;
452: PetscFunctionBegin;
453: PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container));
454: PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private");
455: PetscCall(PetscContainerGetPointer(container, (void **)&info));
456: if (info->init) PetscFunctionReturn(PETSC_SUCCESS);
458: /* Configure window */
459: if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1]));
460: if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name));
462: /* Configure default view */
463: if (mesh) {
464: switch (dim) {
465: case 1:
466: PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
467: break;
468: case 2:
469: PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
470: break;
471: case 3: /* TODO: decide default view in 3D */
472: break;
473: }
474: } else {
475: PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */
476: switch (dim) {
477: case 1:
478: PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */
479: break;
480: case 2:
481: PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */
482: break;
483: case 3:
484: break;
485: }
486: PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */
487: }
489: { /* Additional keys and commands */
490: char keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = "";
491: PetscOptions opt = ((PetscObject)viewer)->options;
492: const char *pre = ((PetscObject)viewer)->prefix;
494: PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL));
495: PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL));
496: if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys));
497: if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds));
498: }
500: /* Pause visualization */
501: if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n"));
502: if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n"));
504: info->init = PETSC_TRUE;
505: PetscFunctionReturn(PETSC_SUCCESS);
506: }
508: static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
509: {
510: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
511: PetscInt i;
513: PetscFunctionBegin;
514: for (i = 0; i < socket->nwindow; i++) {
515: PetscCall(PetscViewerDestroy(&socket->window[i]));
516: PetscCall(PetscFree(socket->windowtitle[i]));
517: PetscCall(PetscFree(socket->fec_type[i]));
518: PetscCall(PetscObjectDestroy(&socket->Ufield[i]));
519: }
520: PetscCall(PetscFree(socket->name));
521: PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield));
522: PetscCall(PetscFree(socket->fmt));
523: PetscCall(PetscViewerDestroy(&socket->meshwindow));
524: PetscCall(PetscObjectDestroy(&socket->dm));
525: if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));
527: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL));
528: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL));
529: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL));
530: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
531: PetscCall(PetscFree(socket));
532: viewer->data = NULL;
533: PetscFunctionReturn(PETSC_SUCCESS);
534: }
536: static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject)
537: {
538: PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
539: PetscInt nsizes = 2, prec = PETSC_DECIDE;
540: PetscBool set;
542: PetscFunctionBegin;
543: PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options");
544: PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set));
545: if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec));
546: PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set));
547: if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0];
548: PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL));
549: PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, &set));
550: PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, &set));
551: PetscOptionsHeadEnd();
552: PetscFunctionReturn(PETSC_SUCCESS);
553: }
555: static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
556: {
557: char *sport = NULL;
558: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
560: PetscFunctionBegin;
561: socket->type = PETSC_VIEWER_GLVIS_DUMP;
562: /* we accept localhost^port */
563: PetscCall(PetscFree(socket->name));
564: PetscCall(PetscStrallocpy(name, &socket->name));
565: PetscCall(PetscStrchr(socket->name, '^', &sport));
566: if (sport) {
567: PetscInt port = 19916;
568: size_t len;
569: PetscErrorCode ierr;
571: *sport++ = 0;
572: PetscCall(PetscStrlen(sport, &len));
573: ierr = PetscOptionsStringToInt(sport, &port);
574: if (PetscUnlikely(ierr)) {
575: socket->port = 19916;
576: } else {
577: socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916;
578: }
579: socket->type = PETSC_VIEWER_GLVIS_SOCKET;
580: }
581: PetscFunctionReturn(PETSC_SUCCESS);
582: }
584: /*@
585: PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`
587: Collective; No Fortran Support
589: Input Parameters:
590: + comm - the MPI communicator
591: . type - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file
592: . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations
593: - port - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP`
595: Output Parameter:
596: . viewer - the `PetscViewer` object
598: Options Database Keys:
599: + -glvis_precision <precision> - Sets number of digits for floating point values
600: . -glvis_size <width,height> - Sets the window size (in pixels)
601: . -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization
602: (0 is default, -1 implies every visualization)
603: . -glvis_keys - Additional keys to configure visualization
604: - -glvis_exec - Additional commands to configure visualization
606: Level: beginner
608: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType`
609: @*/
610: PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer)
611: {
612: PetscViewerGLVis socket;
614: PetscFunctionBegin;
615: PetscCall(PetscViewerCreate(comm, viewer));
616: PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));
618: socket = (PetscViewerGLVis)((*viewer)->data);
619: socket->type = type;
620: if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
621: PetscCall(PetscFree(socket->name));
622: PetscCall(PetscStrallocpy(name, &socket->name));
623: }
624: socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;
626: PetscCall(PetscViewerSetFromOptions(*viewer));
627: PetscFunctionReturn(PETSC_SUCCESS);
628: }
630: /*@C
631: PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.
633: Collective; No Fortran Support
635: Input Parameter:
636: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`
638: Environmental variables:
639: + `PETSC_VIEWER_GLVIS_FILENAME` - output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`)
640: . `PETSC_VIEWER_GLVIS_HOSTNAME` - machine where the GLVis server is listening (defaults to localhost)
641: - `PETSC_VIEWER_GLVIS_PORT` - port opened by the GLVis server (defaults to 19916)
643: Level: intermediate
645: Note:
646: Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return
647: an error code. It is usually used in the form
648: .vb
649: XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));
650: .ve
652: Developer Note:
653: How come this viewer is not stashed as an attribute in the MPI communicator?
655: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
656: @*/
657: PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm)
658: {
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 */
665: PetscFunctionBegin;
666: PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg));
667: if (!flg) {
668: type = PETSC_VIEWER_GLVIS_SOCKET;
669: PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg));
670: if (!flg) { PetscCallNull(PetscStrncpy(fname, "localhost", sizeof(fname))); }
671: PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg));
672: if (flg) { PetscCallNull(PetscOptionsStringToInt(sport, &port)); }
673: } else {
674: type = PETSC_VIEWER_GLVIS_DUMP;
675: }
676: PetscCallNull(PetscViewerGLVisOpen(comm, type, fname, port, &viewer));
677: PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer));
678: PetscFunctionReturn(viewer);
679: }
681: PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
682: {
683: PetscViewerGLVis socket;
685: PetscFunctionBegin;
686: PetscCall(PetscNew(&socket));
688: /* defaults to socket viewer */
689: PetscCall(PetscStrallocpy("localhost", &socket->name));
690: socket->port = 19916; /* GLVis default listening port */
691: socket->type = PETSC_VIEWER_GLVIS_SOCKET;
692: socket->pause = 0; /* just pause the first time */
694: socket->windowsizes[0] = 600;
695: socket->windowsizes[1] = 600;
697: /* defaults to full precision */
698: PetscCall(PetscStrallocpy(" %g", &socket->fmt));
700: viewer->data = (void *)socket;
701: viewer->ops->destroy = PetscViewerDestroy_GLVis;
702: viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
704: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis));
705: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis));
706: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis));
707: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis));
708: PetscFunctionReturn(PETSC_SUCCESS);
709: }
711: /* this is a private implementation of a SOCKET with ASCII data format
712: GLVis does not currently handle binary socket streams */
713: #if defined(PETSC_HAVE_UNISTD_H)
714: #include <unistd.h>
715: #endif
717: #ifndef PETSC_HAVE_WINDOWS_H
718: static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
720: static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
721: {
722: FILE *stream;
724: PetscFunctionBegin;
725: PetscCall(PetscViewerASCIIGetPointer(viewer, &stream));
726: if (stream) {
727: int retv = fclose(stream);
728: PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream");
729: }
730: PetscCall(PetscViewerDestroy_ASCII(viewer));
731: PetscFunctionReturn(PETSC_SUCCESS);
732: }
733: #endif
735: /*
736: This attempts to return a NULL viewer if it is unable to open a socket connection.
738: The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
740: The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value
741: of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
742: */
743: static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer)
744: {
745: #if defined(PETSC_HAVE_WINDOWS_H)
746: PetscFunctionBegin;
747: SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows");
748: #else
749: FILE *stream = NULL;
750: int fd = 0;
751: PetscErrorCode ierr;
753: PetscFunctionBegin;
754: PetscAssertPointer(hostname, 2);
755: PetscAssertPointer(viewer, 4);
756: #if defined(PETSC_USE_SOCKET_VIEWER)
757: ierr = PetscOpenSocket(hostname, (int)port, &fd);
758: #else
759: SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
760: #endif
761: /*
762: The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
763: The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value
764: of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
765: */
766: if (PetscUnlikely(ierr)) {
767: PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
768: *viewer = NULL;
769: PetscFunctionReturn(PETSC_SUCCESS);
770: } else {
771: PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
772: }
773: stream = fdopen(fd, "w"); /* Not possible on Windows */
774: PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
775: PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
776: PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
777: (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket;
778: #endif
779: PetscFunctionReturn(PETSC_SUCCESS);
780: }
782: #if !defined(PETSC_MISSING_SIGPIPE)
784: #include <signal.h>
786: #if defined(PETSC_HAVE_WINDOWS_H)
787: #define PETSC_DEVNULL "NUL"
788: #else
789: #define PETSC_DEVNULL "/dev/null"
790: #endif
792: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
794: static void (*PetscGLVisSigHandler_save)(int) = NULL;
796: static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
797: {
798: PetscGLVisBrokenPipe = PETSC_TRUE;
799: #if !defined(PETSC_MISSING_SIG_IGN)
800: signal(SIGPIPE, SIG_IGN);
801: #endif
802: }
804: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
805: {
806: PetscFunctionBegin;
807: PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
808: PetscGLVisBrokenPipe = PETSC_FALSE;
809: PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
810: PetscFunctionReturn(PETSC_SUCCESS);
811: }
813: PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
814: {
815: PetscBool flag, brokenpipe;
817: PetscFunctionBegin;
818: flag = PetscGLVisBrokenPipe;
819: PetscCallMPI(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
820: if (brokenpipe) {
821: FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
822: PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
823: PetscCall(PetscViewerASCIISetFILE(*win, null));
824: PetscCall(PetscViewerDestroy(win));
825: if (sock) (void)fclose(sock);
826: }
827: (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
828: PetscGLVisSigHandler_save = NULL;
829: PetscGLVisBrokenPipe = PETSC_FALSE;
830: PetscFunctionReturn(PETSC_SUCCESS);
831: }
833: #else
835: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
836: {
837: PetscFunctionBegin;
838: PetscFunctionReturn(PETSC_SUCCESS);
839: }
841: PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
842: {
843: PetscFunctionBegin;
844: PetscFunctionReturn(PETSC_SUCCESS);
845: }
847: #endif