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, MPI_C_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, MPI_C_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, MPI_C_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