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(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(PETSC_SUCCESS);
209: }
211: static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view)
212: {
213: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
214: PetscViewer window = NULL;
215: PetscBool ldis, dis;
217: PetscFunctionBegin;
218: PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window));
219: /* if we could not establish 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(PETSC_SUCCESS);
228: }
230: PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer)
231: {
232: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
234: PetscFunctionBegin;
235: if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause));
236: PetscFunctionReturn(PETSC_SUCCESS);
237: }
239: /* DM specific support */
240: PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm)
241: {
242: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
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;
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(PETSC_SUCCESS);
257: }
259: PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
260: {
261: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
263: PetscFunctionBegin;
264: PetscAssertPointer(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;
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(PETSC_SUCCESS);
289: }
291: PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view)
292: {
293: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
295: PetscFunctionBegin;
296: PetscAssertPointer(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(PETSC_SUCCESS);
309: }
311: PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type)
312: {
313: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
315: PetscFunctionBegin;
316: PetscAssertPointer(type, 2);
317: *type = socket->type;
318: PetscFunctionReturn(PETSC_SUCCESS);
319: }
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_Internal(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus)
323: {
324: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
326: PetscFunctionBegin;
327: PetscAssertPointer(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;
334: for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++)
335: if (!socket->window[i]) lconn = PETSC_FALSE;
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(PETSC_SUCCESS);
342: }
344: PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm)
345: {
346: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
348: PetscFunctionBegin;
349: *dm = socket->dm;
350: PetscFunctionReturn(PETSC_SUCCESS);
351: }
353: PetscErrorCode PetscViewerGLVisGetFields_Internal(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;
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(PETSC_SUCCESS);
365: }
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_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
372: {
373: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
374: PetscViewerGLVisStatus status;
376: PetscFunctionBegin;
378: PetscAssertPointer(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;
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];
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(PETSC_SUCCESS);
418: }
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_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view)
428: {
429: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
431: PetscFunctionBegin;
434: PetscAssertPointer(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(PETSC_SUCCESS);
448: }
450: /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */
451: PetscErrorCode PetscViewerGLVisInitWindow_Internal(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name)
452: {
453: PetscViewerGLVisInfo info;
454: PetscContainer container;
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(PETSC_SUCCESS);
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));
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: }
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;
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: }
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"));
508: info->init = PETSC_TRUE;
509: PetscFunctionReturn(PETSC_SUCCESS);
510: }
512: static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer)
513: {
514: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
515: PetscInt i;
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));
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(PETSC_SUCCESS);
538: }
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;
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, &set));
554: PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, &set));
555: PetscOptionsHeadEnd();
556: PetscFunctionReturn(PETSC_SUCCESS);
557: }
559: static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[])
560: {
561: char *sport = NULL;
562: PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data;
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;
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(PETSC_SUCCESS);
586: }
588: /*@C
589: PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer`
591: Collective; No Fortran Support
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`
599: Output Parameter:
600: . viewer - the `PetscViewer` object
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
610: Level: beginner
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;
618: PetscFunctionBegin;
619: PetscCall(PetscViewerCreate(comm, viewer));
620: PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS));
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;
630: PetscCall(PetscViewerSetFromOptions(*viewer));
631: PetscFunctionReturn(PETSC_SUCCESS);
632: }
634: /*@C
635: PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator.
637: Collective; No Fortran Support
639: Input Parameter:
640: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer`
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)
647: Level: intermediate
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));
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 */
665: PetscFunctionBegin;
666: ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg);
667: if (ierr) {
668: ierr = 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: ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
676: PetscFunctionReturn(NULL);
677: }
678: if (!flg) {
679: ierr = PetscStrncpy(fname, "localhost", sizeof(fname));
680: if (ierr) {
681: ierr = 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: ierr = 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: ierr = 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: ierr = 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: ierr = PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
708: PetscFunctionReturn(NULL);
709: }
710: PetscFunctionReturn(viewer);
711: }
713: PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer)
714: {
715: PetscViewerGLVis socket;
717: PetscFunctionBegin;
718: PetscCall(PetscNew(&socket));
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 */
726: socket->windowsizes[0] = 600;
727: socket->windowsizes[1] = 600;
729: /* defaults to full precision */
730: PetscCall(PetscStrallocpy(" %g", &socket->fmt));
732: viewer->data = (void *)socket;
733: viewer->ops->destroy = PetscViewerDestroy_GLVis;
734: viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis;
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(PETSC_SUCCESS);
741: }
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
749: #ifndef PETSC_HAVE_WINDOWS_H
750: static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer);
752: static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer)
753: {
754: FILE *stream;
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(PETSC_SUCCESS);
764: }
765: #endif
767: /*
768: This attempts to return a NULL viewer if it is unable to open a socket connection.
770: The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc.
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;
785: PetscFunctionBegin;
786: PetscAssertPointer(hostname, 2);
787: PetscAssertPointer(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: PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port));
800: *viewer = NULL;
801: PetscFunctionReturn(PETSC_SUCCESS);
802: } else {
803: PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port));
804: }
805: stream = fdopen(fd, "w"); /* Not possible on Windows */
806: PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port);
807: PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer));
808: PetscViewerDestroy_ASCII = (*viewer)->ops->destroy;
809: (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket;
810: #endif
811: PetscFunctionReturn(PETSC_SUCCESS);
812: }
814: #if !defined(PETSC_MISSING_SIGPIPE)
816: #include <signal.h>
818: #if defined(PETSC_HAVE_WINDOWS_H)
819: #define PETSC_DEVNULL "NUL"
820: #else
821: #define PETSC_DEVNULL "/dev/null"
822: #endif
824: static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE;
826: static void (*PetscGLVisSigHandler_save)(int) = NULL;
828: static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig)
829: {
830: PetscGLVisBrokenPipe = PETSC_TRUE;
831: #if !defined(PETSC_MISSING_SIG_IGN)
832: signal(SIGPIPE, SIG_IGN);
833: #endif
834: }
836: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
837: {
838: PetscFunctionBegin;
839: PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME);
840: PetscGLVisBrokenPipe = PETSC_FALSE;
841: PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE);
842: PetscFunctionReturn(PETSC_SUCCESS);
843: }
845: PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win)
846: {
847: PetscBool flag, brokenpipe;
849: PetscFunctionBegin;
850: flag = PetscGLVisBrokenPipe;
851: PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm));
852: if (brokenpipe) {
853: FILE *sock, *null = fopen(PETSC_DEVNULL, "w");
854: PetscCall(PetscViewerASCIIGetPointer(*win, &sock));
855: PetscCall(PetscViewerASCIISetFILE(*win, null));
856: PetscCall(PetscViewerDestroy(win));
857: if (sock) (void)fclose(sock);
858: }
859: (void)signal(SIGPIPE, PetscGLVisSigHandler_save);
860: PetscGLVisSigHandler_save = NULL;
861: PetscGLVisBrokenPipe = PETSC_FALSE;
862: PetscFunctionReturn(PETSC_SUCCESS);
863: }
865: #else
867: PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
868: {
869: PetscFunctionBegin;
870: PetscFunctionReturn(PETSC_SUCCESS);
871: }
873: PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win)
874: {
875: PetscFunctionBegin;
876: PetscFunctionReturn(PETSC_SUCCESS);
877: }
879: #endif