#define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fdopen() */ #include /*I "petscviewer.h" I*/ #include /*I "petscsys.h" I*/ #include /* we may eventually make this function public */ static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm, const char *, PetscInt, PetscViewer *); struct _n_PetscViewerGLVis { PetscViewerGLVisStatus status; PetscViewerGLVisType type; /* either PETSC_VIEWER_GLVIS_DUMP or PETSC_VIEWER_GLVIS_SOCKET */ char *name; /* prefix for filename, or hostname, depending on the type */ PetscInt port; /* used just for the socket case */ PetscReal pause; /* if positive, calls PetscSleep(pause) after each VecView_GLVis call */ PetscViewer meshwindow; /* used just by the ASCII dumping */ PetscObject dm; /* DM as passed by PetscViewerGLVisSetDM_Private(): should contain discretization info */ PetscInt nwindow; /* number of windows/fields to be visualized */ PetscViewer *window; char **windowtitle; PetscInt windowsizes[2]; char **fec_type; /* type of elements to be used for visualization, see FiniteElementCollection::Name() */ PetscErrorCode (*g2lfield)(PetscObject, PetscInt, PetscObject[], void *); /* global to local operation for generating dofs to be visualized */ PetscInt *spacedim; /* geometrical space dimension (just used to initialize the scene) */ PetscObject *Ufield; /* work vectors for visualization */ PetscInt snapid; /* snapshot id, use PetscViewerGLVisSetSnapId to change this value*/ void *userctx; /* User context, used by g2lfield */ PetscErrorCode (*destroyctx)(void *); /* destroy routine for userctx */ char *fmt; /* format string for FP values */ }; typedef struct _n_PetscViewerGLVis *PetscViewerGLVis; /*@ PetscViewerGLVisSetPrecision - Set the number of digits for floating point values to be displayed Not Collective Input Parameters: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` - prec - the number of digits required Level: beginner .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` @*/ PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec) { PetscFunctionBegin; PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec)); PetscFunctionReturn(0); } static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscCall(PetscFree(socket->fmt)); if (prec > 0) { PetscCall(PetscMalloc1(16, &socket->fmt)); PetscCall(PetscSNPrintf(socket->fmt, 16, " %%.%" PetscInt_FMT "e", prec)); } else { PetscCall(PetscStrallocpy(" %g", &socket->fmt)); } PetscFunctionReturn(0); } /*@ PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP` Logically Collective on viewer Input Parameters: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` - id - the current snapshot id in a time-dependent simulation Level: beginner .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` @*/ PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id) { PetscFunctionBegin; PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); PetscValidLogicalCollectiveInt(viewer, id, 2); PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id)); PetscFunctionReturn(0); } static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; socket->snapid = id; PetscFunctionReturn(0); } /*@C PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector. Logically Collective on viewer Input Parameters: + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` . nf - number of fields to be visualized . fec_type - the type of finite element to be used to visualize the data (see FiniteElementCollection::Name() in MFEM) . dim - array of space dimension for field vectors (used to initialize the scene) . g2lfields - User routine to compute the local field vectors to be visualized; PetscObject is used in place of Vec on the prototype . Vfield - array of work vectors, one for each field . ctx - User context to store the relevant data to apply g2lfields - destroyctx - Destroy function for userctx Notes: g2lfields is called on the vector V to be visualized in order to extract the relevant dofs to be put in Vfield[], as .vb g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx). .ve For vector spaces, the block size of Vfield[i] represents the vector dimension. It misses the Fortran bindings. The names of the Vfield vectors will be displayed in the window title. Level: intermediate .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscObjectSetName()` @*/ 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 *)) { PetscFunctionBegin; PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); PetscValidLogicalCollectiveInt(viewer, nf, 2); PetscCheck(fec_type, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "You need to provide the FiniteElementCollection names for the fields"); PetscValidPointer(fec_type, 3); PetscValidIntPointer(dim, 4); PetscValidPointer(Vfield, 6); 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)); PetscFunctionReturn(0); } 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 *)) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscInt i; PetscFunctionBegin; 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); if (!socket->nwindow) { socket->nwindow = nfields; PetscCall(PetscCalloc5(nfields, &socket->window, nfields, &socket->windowtitle, nfields, &socket->fec_type, nfields, &socket->spacedim, nfields, &socket->Ufield)); for (i = 0; i < nfields; i++) { const char *name; PetscCall(PetscObjectGetName(Vfield[i], &name)); PetscCall(PetscStrallocpy(name, &socket->windowtitle[i])); PetscCall(PetscStrallocpy(fec_type[i], &socket->fec_type[i])); PetscCall(PetscObjectReference(Vfield[i])); socket->Ufield[i] = Vfield[i]; socket->spacedim[i] = dim[i]; } } /* number of fields are not allowed to vary */ PetscCheck(nfields == socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot visualize %" PetscInt_FMT " fields using %" PetscInt_FMT " socket windows", nfields, socket->nwindow); socket->g2lfield = g2l; if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); socket->userctx = ctx; socket->destroyctx = destroyctx; PetscFunctionReturn(0); } static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr) { PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr; PetscFunctionBegin; PetscCall(PetscFree(info->fmt)); PetscCall(PetscFree(info)); PetscFunctionReturn(0); } /* we can decide to prevent specific processes from using the viewer */ static PetscErrorCode PetscViewerGLVisAttachInfo_Private(PetscViewer viewer, PetscViewer window) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscContainer container; PetscViewerGLVisInfo info; PetscFunctionBegin; PetscCall(PetscObjectQuery((PetscObject)window, "_glvis_info_container", (PetscObject *)&container)); if (!container) { PetscCall(PetscNew(&info)); info->enabled = PETSC_TRUE; info->init = PETSC_FALSE; info->size[0] = socket->windowsizes[0]; info->size[1] = socket->windowsizes[1]; info->pause = socket->pause; PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)window), &container)); PetscCall(PetscContainerSetPointer(container, (void *)info)); PetscCall(PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private)); PetscCall(PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container)); PetscCall(PetscContainerDestroy(&container)); } else { PetscCall(PetscContainerGetPointer(container, (void **)&info)); } PetscCall(PetscFree(info->fmt)); PetscCall(PetscStrallocpy(socket->fmt, &info->fmt)); PetscFunctionReturn(0); } static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscViewer window = NULL; PetscBool ldis, dis; PetscFunctionBegin; PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window)); /* if we could not estabilish a connection, we disable the socket viewer on all MPI ranks */ ldis = !viewer ? PETSC_TRUE : PETSC_FALSE; PetscCall(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer))); if (dis) { socket->status = PETSCVIEWERGLVIS_DISABLED; PetscCall(PetscViewerDestroy(&window)); } *view = window; PetscFunctionReturn(0); } PetscErrorCode PetscViewerGLVisPause_Private(PetscViewer viewer) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause)); PetscFunctionReturn(0); } /* DM specific support */ PetscErrorCode PetscViewerGLVisSetDM_Private(PetscViewer viewer, PetscObject dm) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer"); if (!socket->dm) { PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL; PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm)); if (setupwithdm) { PetscCall((*setupwithdm)(dm, viewer)); } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name); PetscCall(PetscObjectReference(dm)); socket->dm = dm; } PetscFunctionReturn(0); } PetscErrorCode PetscViewerGLVisGetDMWindow_Private(PetscViewer viewer, PetscViewer *view) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscValidPointer(view, 2); if (!socket->meshwindow) { if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) { PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow)); } else { size_t len; PetscBool isstdout; PetscCall(PetscStrlen(socket->name, &len)); PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); if (!socket->name || !len || isstdout) { PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow)); } else { PetscMPIInt rank; char filename[PETSC_MAX_PATH_LEN]; PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank)); PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow)); } } if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS)); } if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow)); *view = socket->meshwindow; PetscFunctionReturn(0); } PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer, PetscViewer *view) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscValidPointer(view, 2); PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()"); if (*view) { PetscCall(PetscViewerFlush(*view)); PetscCall(PetscBarrier((PetscObject)viewer)); } if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ PetscCall(PetscViewerDestroy(&socket->meshwindow)); } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ socket->meshwindow = NULL; } *view = NULL; PetscFunctionReturn(0); } PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer, PetscViewerGLVisType *type) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscValidPointer(type, 2); *type = socket->type; PetscFunctionReturn(0); } /* 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 */ PetscErrorCode PetscViewerGLVisGetStatus_Private(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscValidPointer(sockstatus, 2); if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { socket->status = PETSCVIEWERGLVIS_DISCONNECTED; } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) { PetscInt i; PetscBool lconn, conn; for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++) if (!socket->window[i]) lconn = PETSC_FALSE; PetscCall(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer))); if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED; } *sockstatus = socket->status; PetscFunctionReturn(0); } PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject *dm) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; *dm = socket->dm; PetscFunctionReturn(0); } PetscErrorCode PetscViewerGLVisGetFields_Private(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; if (nfield) *nfield = socket->nwindow; if (fec) *fec = (const char **)socket->fec_type; if (spacedim) *spacedim = socket->spacedim; if (g2lfield) *g2lfield = socket->g2lfield; if (Ufield) *Ufield = socket->Ufield; if (userctx) *userctx = socket->userctx; PetscFunctionReturn(0); } /* accessor routines for the viewer windows: PETSC_VIEWER_GLVIS_DUMP : it returns a new viewer every time PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done. */ PetscErrorCode PetscViewerGLVisGetWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscViewerGLVisStatus status; PetscFunctionBegin; PetscValidLogicalCollectiveInt(viewer, wid, 2); PetscValidPointer(view, 3); 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); status = socket->status; 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); switch (status) { case PETSCVIEWERGLVIS_DISCONNECTED: PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen"); if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { size_t len; PetscBool isstdout; PetscCall(PetscStrlen(socket->name, &len)); PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); if (!socket->name || !len || isstdout) { PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid])); } else { PetscMPIInt rank; char filename[PETSC_MAX_PATH_LEN]; PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank)); PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid])); } } else { PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid])); } if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS)); *view = socket->window[wid]; break; case PETSCVIEWERGLVIS_CONNECTED: *view = socket->window[wid]; break; case PETSCVIEWERGLVIS_DISABLED: *view = NULL; break; default: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status); } if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view)); PetscFunctionReturn(0); } /* Restore the window viewer PETSC_VIEWER_GLVIS_DUMP : destroys the temporary created ASCII viewer used for dumping PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer. - it the returned window viewer is NULL, assumes something went wrong with the socket (i.e. SIGPIPE when a user closes the popup window) and that the caller already handled it (see VecView_GLVis). */ PetscErrorCode PetscViewerGLVisRestoreWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS); PetscValidLogicalCollectiveInt(viewer, wid, 2); PetscValidPointer(view, 3); 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); PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()"); if (*view) { PetscCall(PetscViewerFlush(*view)); PetscCall(PetscBarrier((PetscObject)viewer)); } if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ PetscCall(PetscViewerDestroy(&socket->window[wid])); } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ socket->window[wid] = NULL; } *view = NULL; PetscFunctionReturn(0); } /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */ PetscErrorCode PetscViewerGLVisInitWindow_Private(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name) { PetscViewerGLVisInfo info; PetscContainer container; PetscFunctionBegin; PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container)); PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private"); PetscCall(PetscContainerGetPointer(container, (void **)&info)); if (info->init) PetscFunctionReturn(0); /* Configure window */ if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1])); if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name)); /* Configure default view */ if (mesh) { switch (dim) { case 1: PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ break; case 2: PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ break; case 3: /* TODO: decide default view in 3D */ break; } } else { PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */ switch (dim) { case 1: PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */ break; case 2: PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */ break; case 3: break; } PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */ } { /* Additional keys and commands */ char keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = ""; PetscOptions opt = ((PetscObject)viewer)->options; const char *pre = ((PetscObject)viewer)->prefix; PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL)); PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL)); if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys)); if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds)); } /* Pause visualization */ if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n")); if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n")); info->init = PETSC_TRUE; PetscFunctionReturn(0); } static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer) { PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscInt i; PetscFunctionBegin; for (i = 0; i < socket->nwindow; i++) { PetscCall(PetscViewerDestroy(&socket->window[i])); PetscCall(PetscFree(socket->windowtitle[i])); PetscCall(PetscFree(socket->fec_type[i])); PetscCall(PetscObjectDestroy(&socket->Ufield[i])); } PetscCall(PetscFree(socket->name)); PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield)); PetscCall(PetscFree(socket->fmt)); PetscCall(PetscViewerDestroy(&socket->meshwindow)); PetscCall(PetscObjectDestroy(&socket->dm)); if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL)); PetscCall(PetscFree(socket)); viewer->data = NULL; PetscFunctionReturn(0); } static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject) { PetscViewerGLVis socket = (PetscViewerGLVis)v->data; PetscInt nsizes = 2, prec = PETSC_DECIDE; PetscBool set; PetscFunctionBegin; PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options"); PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set)); if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec)); PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set)); if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0]; PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL)); PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, NULL)); PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, NULL)); PetscOptionsHeadEnd(); PetscFunctionReturn(0); } static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) { char *sport; PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; PetscFunctionBegin; socket->type = PETSC_VIEWER_GLVIS_DUMP; /* we accept localhost^port */ PetscCall(PetscFree(socket->name)); PetscCall(PetscStrallocpy(name, &socket->name)); PetscCall(PetscStrchr(socket->name, '^', &sport)); if (sport) { PetscInt port = 19916; size_t len; PetscErrorCode ierr; *sport++ = 0; PetscCall(PetscStrlen(sport, &len)); ierr = PetscOptionsStringToInt(sport, &port); if (PetscUnlikely(ierr)) { socket->port = 19916; } else { socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916; } socket->type = PETSC_VIEWER_GLVIS_SOCKET; } PetscFunctionReturn(0); } /*@C PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer` Collective Input Parameters: + comm - the MPI communicator . type - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations - port - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP` Output Parameters: - viewer - the `PetscViewer` object Options Database Keys: + -glvis_precision - Sets number of digits for floating point values . -glvis_size - Sets the window size (in pixels) . -glvis_pause - Sets time (in seconds) that the program pauses after each visualization (0 is default, -1 implies every visualization) . -glvis_keys - Additional keys to configure visualization - -glvis_exec - Additional commands to configure visualization Fortran Note: Missing Fortran binding Level: beginner .seealso: `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType` @*/ PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer) { PetscViewerGLVis socket; PetscFunctionBegin; PetscCall(PetscViewerCreate(comm, viewer)); PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS)); socket = (PetscViewerGLVis)((*viewer)->data); socket->type = type; if (type == PETSC_VIEWER_GLVIS_DUMP || name) { PetscCall(PetscFree(socket->name)); PetscCall(PetscStrallocpy(name, &socket->name)); } socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port; PetscCall(PetscViewerSetFromOptions(*viewer)); PetscFunctionReturn(0); } /* PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator. Collective Input Parameter: . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer` Level: intermediate Environmental variables: + `PETSC_VIEWER_GLVIS_FILENAME` : output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`) . `PETSC_VIEWER_GLVIS_HOSTNAME` : machine where the GLVis server is listening (defaults to localhost) - `PETSC_VIEWER_GLVIS_PORT` : port opened by the GLVis server (defaults to 19916) Note: Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return an error code. It is usually used in the form $ XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm)); Fortran Note: Missing Fortran bindings .seealso: `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()` */ PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) { PetscErrorCode ierr; PetscBool flg; PetscViewer viewer; PetscViewerGLVisType type; char fname[PETSC_MAX_PATH_LEN], sport[16]; PetscInt port = 19916; /* default for GLVis */ PetscFunctionBegin; ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } if (!flg) { type = PETSC_VIEWER_GLVIS_SOCKET; ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } if (!flg) { ierr = PetscStrcpy(fname, "localhost"); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } } ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } if (flg) { ierr = PetscOptionsStringToInt(sport, &port); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } } } else { type = PETSC_VIEWER_GLVIS_DUMP; } ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } ierr = PetscObjectRegisterDestroy((PetscObject)viewer); if (ierr) { PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); PetscFunctionReturn(NULL); } PetscFunctionReturn(viewer); } PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) { PetscViewerGLVis socket; PetscFunctionBegin; PetscCall(PetscNew(&socket)); /* defaults to socket viewer */ PetscCall(PetscStrallocpy("localhost", &socket->name)); socket->port = 19916; /* GLVis default listening port */ socket->type = PETSC_VIEWER_GLVIS_SOCKET; socket->pause = 0; /* just pause the first time */ socket->windowsizes[0] = 600; socket->windowsizes[1] = 600; /* defaults to full precision */ PetscCall(PetscStrallocpy(" %g", &socket->fmt)); viewer->data = (void *)socket; viewer->ops->destroy = PetscViewerDestroy_GLVis; viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis)); PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis)); PetscFunctionReturn(0); } /* this is a private implementation of a SOCKET with ASCII data format GLVis does not currently handle binary socket streams */ #if defined(PETSC_HAVE_UNISTD_H) #include #endif #ifndef PETSC_HAVE_WINDOWS_H static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) { FILE *stream; PetscFunctionBegin; PetscCall(PetscViewerASCIIGetPointer(viewer, &stream)); if (stream) { int retv = fclose(stream); PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream"); } PetscCall(PetscViewerDestroy_ASCII(viewer)); PetscFunctionReturn(0); } #endif /* This attempts to return a NULL viewer if it is unable to open a socket connection. The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. */ static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) { #if defined(PETSC_HAVE_WINDOWS_H) PetscFunctionBegin; SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows"); #else FILE *stream = NULL; int fd = 0; PetscErrorCode ierr; PetscFunctionBegin; PetscValidCharPointer(hostname, 2); PetscValidPointer(viewer, 4); #if defined(PETSC_USE_SOCKET_VIEWER) ierr = PetscOpenSocket(hostname, port, &fd); #else SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer"); #endif /* The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. */ if (PetscUnlikely(ierr)) { char err[1024]; PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port)); PetscCall(PetscInfo(NULL, "%s", err)); *viewer = NULL; PetscFunctionReturn(0); } else { char msg[1024]; PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port)); PetscCall(PetscInfo(NULL, "%s", msg)); } stream = fdopen(fd, "w"); /* Not possible on Windows */ PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port); PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer)); PetscViewerDestroy_ASCII = (*viewer)->ops->destroy; (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket; #endif PetscFunctionReturn(0); } #if !defined(PETSC_MISSING_SIGPIPE) #include #if defined(PETSC_HAVE_WINDOWS_H) #define PETSC_DEVNULL "NUL" #else #define PETSC_DEVNULL "/dev/null" #endif static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; static void (*PetscGLVisSigHandler_save)(int) = NULL; static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) { PetscGLVisBrokenPipe = PETSC_TRUE; #if !defined(PETSC_MISSING_SIG_IGN) signal(SIGPIPE, SIG_IGN); #endif } PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { PetscFunctionBegin; PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME); PetscGLVisBrokenPipe = PETSC_FALSE; PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE); PetscFunctionReturn(0); } PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) { PetscBool flag, brokenpipe; PetscFunctionBegin; flag = PetscGLVisBrokenPipe; PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm)); if (brokenpipe) { FILE *sock, *null = fopen(PETSC_DEVNULL, "w"); PetscCall(PetscViewerASCIIGetPointer(*win, &sock)); PetscCall(PetscViewerASCIISetFILE(*win, null)); PetscCall(PetscViewerDestroy(win)); if (sock) (void)fclose(sock); } (void)signal(SIGPIPE, PetscGLVisSigHandler_save); PetscGLVisSigHandler_save = NULL; PetscGLVisBrokenPipe = PETSC_FALSE; PetscFunctionReturn(0); } #else PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { PetscFunctionBegin; PetscFunctionReturn(0); } PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { PetscFunctionBegin; PetscFunctionReturn(0); } #endif