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: PetscCheck(setupwithdm, PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name);
247: PetscCall((*setupwithdm)(dm, viewer));
248: PetscCall(PetscObjectReference(dm));
249: socket->dm = dm;
250: }
251: PetscFunctionReturn(PETSC_SUCCESS);
252: }
254: PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
255: {
256: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
258: PetscFunctionBegin;
259: PetscAssertPointer(view, 2);
260: if (!socket->meshwindow) {
261: if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) {
262: PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow));
263: } else {
264: size_t len;
265: PetscBool isstdout;
267: PetscCall(PetscStrlen(socket->name, &len));
268: PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
269: if (!socket->name || !len || isstdout) {
270: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow));
271: } else {
272: PetscMPIInt rank;
273: char filename[PETSC_MAX_PATH_LEN];
274: PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
275: PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank));
276: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow));
277: }
278: }
279: if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS));
280: }
281: if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow));
282: *view = socket->meshwindow;
283: PetscFunctionReturn(PETSC_SUCCESS);
284: }
286: PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
287: {
288: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
290: PetscFunctionBegin;
291: PetscAssertPointer(view, 2);
292: PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()");
293: if (*view) {
294: PetscCall(PetscViewerFlush(*view));
295: PetscCall(PetscBarrier((PetscObject)viewer));
296: }
297: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
298: PetscCall(PetscViewerDestroy(&socket->meshwindow));
299: } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
300: socket->meshwindow = NULL;
301: }
302: *view = NULL;
303: PetscFunctionReturn(PETSC_SUCCESS);
304: }
306: PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
307: {
308: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
310: PetscFunctionBegin;
311: PetscAssertPointer(type, 2);
312: *type = socket->type;
313: PetscFunctionReturn(PETSC_SUCCESS);
314: }
316: /* 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 */
317: PetscErrorCode PetscViewerGLVisGetStatus_Internal(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
318: {
319: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
321: PetscFunctionBegin;
322: PetscAssertPointer(sockstatus, 2);
323: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
324: socket->status = PETSCVIEWERGLVIS_DISCONNECTED;
325: } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) {
326: PetscInt i;
327: PetscBool lconn, conn;
329: for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
330: if (!socket->window[i]) lconn = PETSC_FALSE;
332: PetscCallMPI(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer)));
333: if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED;
334: }
335: *sockstatus = socket->status;
336: PetscFunctionReturn(PETSC_SUCCESS);
337: }
339: PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
340: {
341: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
343: PetscFunctionBegin;
344: *dm = socket->dm;
345: PetscFunctionReturn(PETSC_SUCCESS);
346: }
348: PetscErrorCode PetscViewerGLVisGetFields_Internal(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx)
349: {
350: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
352: PetscFunctionBegin;
353: if (nfield) *nfield = socket->nwindow;
354: if (fec) *fec = (const char **)socket->fec_type;
355: if (spacedim) *spacedim = socket->spacedim;
356: if (g2lfield) *g2lfield = socket->g2lfield;
357: if (Ufield) *Ufield = socket->Ufield;
358: if (userctx) *userctx = socket->userctx;
359: PetscFunctionReturn(PETSC_SUCCESS);
360: }
362: /* accessor routines for the viewer windows:
363: PETSC_VIEWER_GLVIS_DUMP : it returns a new viewer every time
364: PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done.
365: */
366: PetscErrorCode PetscViewerGLVisGetWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
367: {
368: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
369: PetscViewerGLVisStatus status;
371: PetscFunctionBegin;
373: PetscAssertPointer(view, 3);
374: 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);
375: status = socket->status;
376: 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);
377: switch (status) {
378: case PETSCVIEWERGLVIS_DISCONNECTED:
379: PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen");
380: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) {
381: size_t len;
382: PetscBool isstdout;
384: PetscCall(PetscStrlen(socket->name, &len));
385: PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout));
386: if (!socket->name || !len || isstdout) {
387: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid]));
388: } else {
389: PetscMPIInt rank;
390: char filename[PETSC_MAX_PATH_LEN];
392: PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
393: PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank));
394: PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid]));
395: }
396: } else {
397: PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid]));
398: }
399: if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS));
400: *view = socket->window[wid];
401: break;
402: case PETSCVIEWERGLVIS_CONNECTED:
403: *view = socket->window[wid];
404: break;
405: case PETSCVIEWERGLVIS_DISABLED:
406: *view = NULL;
407: break;
408: default:
409: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status);
410: }
411: if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view));
412: PetscFunctionReturn(PETSC_SUCCESS);
413: }
415: /* Restore the window viewer
416: PETSC_VIEWER_GLVIS_DUMP : destroys the temporary created ASCII viewer used for dumping
417: PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer.
418: - it the returned window viewer is NULL, assumes something went wrong
419: with the socket (i.e. SIGPIPE when a user closes the popup window)
420: and that the caller already handled it (see VecView_GLVis).
421: */
422: PetscErrorCode PetscViewerGLVisRestoreWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
423: {
424: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
426: PetscFunctionBegin;
429: PetscAssertPointer(view, 3);
430: 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);
431: PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()");
432: if (*view) {
433: PetscCall(PetscViewerFlush(*view));
434: PetscCall(PetscBarrier((PetscObject)viewer));
435: }
436: if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */
437: PetscCall(PetscViewerDestroy(&socket->window[wid]));
438: } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */
439: socket->window[wid] = NULL;
440: }
441: *view = NULL;
442: PetscFunctionReturn(PETSC_SUCCESS);
443: }
445: /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
446: PetscErrorCode PetscViewerGLVisInitWindow_Internal(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
447: {
448: PetscViewerGLVisInfo info;
449: PetscContainer container;
451: PetscFunctionBegin;
452: PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container));
453: PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private");
454: PetscCall(PetscContainerGetPointer(container, (void **)&info));
455: if (info->init) PetscFunctionReturn(PETSC_SUCCESS);
457: /* Configure window */
458: if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1]));
459: if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name));
461: /* Configure default view */
462: if (mesh) {
463: switch (dim) {
464: case 1:
465: PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
466: break;
467: case 2:
468: PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */
469: break;
470: case 3: /* TODO: decide default view in 3D */
471: break;
472: }
473: } else {
474: PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */
475: switch (dim) {
476: case 1:
477: PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */
478: break;
479: case 2:
480: PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */
481: break;
482: case 3:
483: break;
484: }
485: PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */
486: }
488: { /* Additional keys and commands */
489: char keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = "";
490: PetscOptions opt = ((PetscObject)viewer)->options;
491: const char *pre = ((PetscObject)viewer)->prefix;
493: PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL));
494: PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL));
495: if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys));
496: if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds));
497: }
499: /* Pause visualization */
500: if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n"));
501: if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n"));
503: info->init = PETSC_TRUE;
504: PetscFunctionReturn(PETSC_SUCCESS);
505: }
507: static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
508: {
509: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
510: PetscInt i;
512: PetscFunctionBegin;
513: for (i = 0; i < socket->nwindow; i++) {
514: PetscCall(PetscViewerDestroy(&socket->window[i]));
515: PetscCall(PetscFree(socket->windowtitle[i]));
516: PetscCall(PetscFree(socket->fec_type[i]));
517: PetscCall(PetscObjectDestroy(&socket->Ufield[i]));
518: }
519: PetscCall(PetscFree(socket->name));
520: PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield));
521: PetscCall(PetscFree(socket->fmt));
522: PetscCall(PetscViewerDestroy(&socket->meshwindow));
523: PetscCall(PetscObjectDestroy(&socket->dm));
524: if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx));
526: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL));
527: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL));
528: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL));
529: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL));
530: PetscCall(PetscFree(socket));
531: viewer->data = NULL;
532: PetscFunctionReturn(PETSC_SUCCESS);
533: }
535: static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems PetscOptionsObject)
536: {
537: PetscViewerGLVis socket = (PetscViewerGLVis)v->data;
538: PetscInt nsizes = 2, prec = PETSC_DECIDE;
539: PetscBool set;
541: PetscFunctionBegin;
542: PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options");
543: PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set));
544: if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec));
545: PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set));
546: if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0];
547: PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL));
548: PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, &set));
549: PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, &set));
550: PetscOptionsHeadEnd();
551: PetscFunctionReturn(PETSC_SUCCESS);
552: }
554: static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
555: {
556: char *sport = NULL;
557: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
559: PetscFunctionBegin;
560: socket->type = PETSC_VIEWER_GLVIS_DUMP;
561: /* we accept localhost^port */
562: PetscCall(PetscFree(socket->name));
563: PetscCall(PetscStrallocpy(name, &socket->name));
564: PetscCall(PetscStrchr(socket->name, '^', &sport));
565: if (sport) {
566: PetscInt port = 19916;
567: size_t len;
568: PetscErrorCode ierr;
570: *sport++ = 0;
571: PetscCall(PetscStrlen(sport, &len));
572: ierr = PetscOptionsStringToInt(sport, &port);
573: if (PetscUnlikely(ierr)) {
574: socket->port = 19916;
575: } else {
576: socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916;
577: }
578: socket->type = PETSC_VIEWER_GLVIS_SOCKET;
579: }
580: PetscFunctionReturn(PETSC_SUCCESS);
581: }
583: /*@
584: PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`
586: Collective; No Fortran Support
588: Input Parameters:
589: + comm - the MPI communicator
590: . type - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file
591: . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations
592: - port - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP`
594: Output Parameter:
595: . viewer - the `PetscViewer` object
597: Options Database Keys:
598: + -glvis_precision <precision> - Sets number of digits for floating point values
599: . -glvis_size <width,height> - Sets the window size (in pixels)
600: . -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization
601: (0 is default, -1 implies every visualization)
602: . -glvis_keys - Additional keys to configure visualization
603: - -glvis_exec - Additional commands to configure visualization
605: Level: beginner
607: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType`
608: @*/
609: PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer)
610: {
611: PetscViewerGLVis socket;
613: PetscFunctionBegin;
614: PetscCall(PetscViewerCreate(comm, viewer));
615: PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));
617: socket = (PetscViewerGLVis)((*viewer)->data);
618: socket->type = type;
619: if (type == PETSC_VIEWER_GLVIS_DUMP || name) {
620: PetscCall(PetscFree(socket->name));
621: PetscCall(PetscStrallocpy(name, &socket->name));
622: }
623: socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port;
625: PetscCall(PetscViewerSetFromOptions(*viewer));
626: PetscFunctionReturn(PETSC_SUCCESS);
627: }
629: /*@C
630: PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.
632: Collective; No Fortran Support
634: Input Parameter:
635: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`
637: Environmental variables:
638: + `PETSC_VIEWER_GLVIS_FILENAME` - output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`)
639: . `PETSC_VIEWER_GLVIS_HOSTNAME` - machine where the GLVis server is listening (defaults to localhost)
640: - `PETSC_VIEWER_GLVIS_PORT` - port opened by the GLVis server (defaults to 19916)
642: Level: intermediate
644: Note:
645: Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return
646: an error code. It is usually used in the form
647: .vb
648: XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm));
649: .ve
651: Developer Note:
652: How come this viewer is not stashed as an attribute in the MPI communicator?
654: .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()`
655: @*/
656: PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm)
657: {
658: PetscBool flg;
659: PetscViewer viewer;
660: PetscViewerGLVisType type;
661: char fname[PETSC_MAX_PATH_LEN], sport[16];
662: PetscInt port = 19916; /* default for GLVis */
664: PetscFunctionBegin;
665: PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg));
666: if (!flg) {
667: type = PETSC_VIEWER_GLVIS_SOCKET;
668: PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg));
669: if (!flg) PetscCallNull(PetscStrncpy(fname, "localhost", sizeof(fname)));
670: PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg));
671: if (flg) PetscCallNull(PetscOptionsStringToInt(sport, &port));
672: } else {
673: type = PETSC_VIEWER_GLVIS_DUMP;
674: }
675: PetscCallNull(PetscViewerGLVisOpen(comm, type, fname, port, &viewer));
676: PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer));
677: PetscFunctionReturn(viewer);
678: }
680: PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
681: {
682: PetscViewerGLVis socket;
684: PetscFunctionBegin;
685: PetscCall(PetscNew(&socket));
687: /* defaults to socket viewer */
688: PetscCall(PetscStrallocpy("localhost", &socket->name));
689: socket->port = 19916; /* GLVis default listening port */
690: socket->type = PETSC_VIEWER_GLVIS_SOCKET;
691: socket->pause = 0; /* just pause the first time */
693: socket->windowsizes[0] = 600;
694: socket->windowsizes[1] = 600;
696: /* defaults to full precision */
697: PetscCall(PetscStrallocpy(" %g", &socket->fmt));
699: viewer->data = (void *)socket;
700: viewer->ops->destroy = PetscViewerDestroy_GLVis;
701: viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
703: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis));
704: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis));
705: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis));
706: PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis));
707: PetscFunctionReturn(PETSC_SUCCESS);
708: }
710: /* this is a private implementation of a SOCKET with ASCII data format
711: GLVis does not currently handle binary socket streams */
712: #if defined(PETSC_HAVE_UNISTD_H)
713: #include <unistd.h>
714: #endif
716: #ifndef PETSC_HAVE_WINDOWS_H
717: static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
719: static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
720: {
721: FILE *stream;
723: PetscFunctionBegin;
724: PetscCall(PetscViewerASCIIGetPointer(viewer, &stream));
725: if (stream) {
726: int retv = fclose(stream);
727: PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream");
728: }
729: PetscCall(PetscViewerDestroy_ASCII(viewer));
730: PetscFunctionReturn(PETSC_SUCCESS);
731: }
732: #endif
734: /*
735: This attempts to return a NULL viewer if it is unable to open a socket connection.
737: The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
739: The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value
740: of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
741: */
742: static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer)
743: {
744: #if defined(PETSC_HAVE_WINDOWS_H)
745: PetscFunctionBegin;
746: SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows");
747: #else
748: FILE *stream = NULL;
749: int fd = 0;
750: PetscErrorCode ierr;
751: PetscMPIInt iport;
753: PetscFunctionBegin;
754: PetscAssertPointer(hostname, 2);
755: PetscAssertPointer(viewer, 4);
756: PetscCall(PetscMPIIntCast(port, &iport));
757: #if defined(PETSC_USE_SOCKET_VIEWER)
758: ierr = PetscOpenSocket(hostname, iport, &fd);
759: #else
760: SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer");
761: #endif
762: /*
763: The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
764: The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value
765: of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened.
766: */
767: if (PetscUnlikely(ierr)) {
768: PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
769: *viewer = NULL;
770: PetscFunctionReturn(PETSC_SUCCESS);
771: } else {
772: PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
773: }
774: stream = fdopen(fd, "w"); /* Not possible on Windows */
775: PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
776: PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
777: PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
778: (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket;
779: #endif
780: PetscFunctionReturn(PETSC_SUCCESS);
781: }
783: #if !defined(PETSC_MISSING_SIGPIPE)
785: #include <signal.h>
787: #if defined(PETSC_HAVE_WINDOWS_H)
788: #define PETSC_DEVNULL "NUL"
789: #else
790: #define PETSC_DEVNULL "/dev/null"
791: #endif
793: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
795: static void (*PetscGLVisSigHandler_save)(int) = NULL;
797: static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
798: {
799: PetscGLVisBrokenPipe = PETSC_TRUE;
800: #if !defined(PETSC_MISSING_SIG_IGN)
801: signal(SIGPIPE, SIG_IGN);
802: #endif
803: }
805: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
806: {
807: PetscFunctionBegin;
808: PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
809: PetscGLVisBrokenPipe = PETSC_FALSE;
810: PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
811: PetscFunctionReturn(PETSC_SUCCESS);
812: }
814: PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
815: {
816: PetscBool flag, brokenpipe;
818: PetscFunctionBegin;
819: flag = PetscGLVisBrokenPipe;
820: PetscCallMPI(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
821: if (brokenpipe) {
822: FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
823: PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
824: PetscCall(PetscViewerASCIISetFILE(*win, null));
825: PetscCall(PetscViewerDestroy(win));
826: if (sock) (void)fclose(sock);
827: }
828: (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
829: PetscGLVisSigHandler_save = NULL;
830: PetscGLVisBrokenPipe = PETSC_FALSE;
831: PetscFunctionReturn(PETSC_SUCCESS);
832: }
834: #else
836: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
837: {
838: PetscFunctionBegin;
839: PetscFunctionReturn(PETSC_SUCCESS);
840: }
842: PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
843: {
844: PetscFunctionBegin;
845: PetscFunctionReturn(PETSC_SUCCESS);
846: }
848: #endif