1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fdopen() */ 2 3 #include <petsc/private/viewerimpl.h> /*I "petscviewer.h" I*/ 4 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/ 5 #include <petsc/private/glvisviewerimpl.h> 6 7 /* we may eventually make this function public */ 8 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm, const char *, PetscInt, PetscViewer *); 9 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; 32 33 /*@ 34 PetscViewerGLVisSetPrecision - Set the number of digits for floating point values to be displayed 35 36 Not Collective 37 38 Input Parameters: 39 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` 40 - prec - the number of digits required 41 42 Level: beginner 43 44 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 45 @*/ 46 PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec) { 47 PetscFunctionBegin; 48 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 49 PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec)); 50 PetscFunctionReturn(0); 51 } 52 53 static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec) { 54 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 55 56 PetscFunctionBegin; 57 PetscCall(PetscFree(socket->fmt)); 58 if (prec > 0) { 59 PetscCall(PetscMalloc1(16, &socket->fmt)); 60 PetscCall(PetscSNPrintf(socket->fmt, 16, " %%.%" PetscInt_FMT "e", prec)); 61 } else { 62 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 63 } 64 PetscFunctionReturn(0); 65 } 66 67 /*@ 68 PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP` 69 70 Logically Collective on viewer 71 72 Input Parameters: 73 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` 74 - id - the current snapshot id in a time-dependent simulation 75 76 Level: beginner 77 78 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 79 @*/ 80 PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id) { 81 PetscFunctionBegin; 82 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 83 PetscValidLogicalCollectiveInt(viewer, id, 2); 84 PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id)); 85 PetscFunctionReturn(0); 86 } 87 88 static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id) { 89 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 90 91 PetscFunctionBegin; 92 socket->snapid = id; 93 PetscFunctionReturn(0); 94 } 95 96 /*@C 97 PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector. 98 99 Logically Collective on viewer 100 101 Input Parameters: 102 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` 103 . nf - number of fields to be visualized 104 . fec_type - the type of finite element to be used to visualize the data (see FiniteElementCollection::Name() in MFEM) 105 . dim - array of space dimension for field vectors (used to initialize the scene) 106 . g2lfields - User routine to compute the local field vectors to be visualized; PetscObject is used in place of Vec on the prototype 107 . Vfield - array of work vectors, one for each field 108 . ctx - User context to store the relevant data to apply g2lfields 109 - destroyctx - Destroy function for userctx 110 111 Notes: 112 g2lfields is called on the vector V to be visualized in order to extract the relevant dofs to be put in Vfield[], as 113 .vb 114 g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx). 115 .ve 116 117 For vector spaces, the block size of Vfield[i] represents the vector dimension. It misses the Fortran bindings. 118 The names of the Vfield vectors will be displayed in the window title. 119 120 Level: intermediate 121 122 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscObjectSetName()` 123 @*/ 124 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 *)) { 125 PetscFunctionBegin; 126 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 127 PetscValidLogicalCollectiveInt(viewer, nf, 2); 128 PetscCheck(fec_type, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "You need to provide the FiniteElementCollection names for the fields"); 129 PetscValidPointer(fec_type, 3); 130 PetscValidIntPointer(dim, 4); 131 PetscValidPointer(Vfield, 6); 132 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)); 133 PetscFunctionReturn(0); 134 } 135 136 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 *)) { 137 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 138 PetscInt i; 139 140 PetscFunctionBegin; 141 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); 142 if (!socket->nwindow) { 143 socket->nwindow = nfields; 144 145 PetscCall(PetscCalloc5(nfields, &socket->window, nfields, &socket->windowtitle, nfields, &socket->fec_type, nfields, &socket->spacedim, nfields, &socket->Ufield)); 146 for (i = 0; i < nfields; i++) { 147 const char *name; 148 149 PetscCall(PetscObjectGetName(Vfield[i], &name)); 150 PetscCall(PetscStrallocpy(name, &socket->windowtitle[i])); 151 PetscCall(PetscStrallocpy(fec_type[i], &socket->fec_type[i])); 152 PetscCall(PetscObjectReference(Vfield[i])); 153 socket->Ufield[i] = Vfield[i]; 154 socket->spacedim[i] = dim[i]; 155 } 156 } 157 /* number of fields are not allowed to vary */ 158 PetscCheck(nfields == socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot visualize %" PetscInt_FMT " fields using %" PetscInt_FMT " socket windows", nfields, socket->nwindow); 159 socket->g2lfield = g2l; 160 if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); 161 socket->userctx = ctx; 162 socket->destroyctx = destroyctx; 163 PetscFunctionReturn(0); 164 } 165 166 static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr) { 167 PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr; 168 169 PetscFunctionBegin; 170 PetscCall(PetscFree(info->fmt)); 171 PetscCall(PetscFree(info)); 172 PetscFunctionReturn(0); 173 } 174 175 /* we can decide to prevent specific processes from using the viewer */ 176 static PetscErrorCode PetscViewerGLVisAttachInfo_Private(PetscViewer viewer, PetscViewer window) { 177 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 178 PetscContainer container; 179 PetscViewerGLVisInfo info; 180 181 PetscFunctionBegin; 182 PetscCall(PetscObjectQuery((PetscObject)window, "_glvis_info_container", (PetscObject *)&container)); 183 if (!container) { 184 PetscCall(PetscNew(&info)); 185 info->enabled = PETSC_TRUE; 186 info->init = PETSC_FALSE; 187 info->size[0] = socket->windowsizes[0]; 188 info->size[1] = socket->windowsizes[1]; 189 info->pause = socket->pause; 190 PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)window), &container)); 191 PetscCall(PetscContainerSetPointer(container, (void *)info)); 192 PetscCall(PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private)); 193 PetscCall(PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container)); 194 PetscCall(PetscContainerDestroy(&container)); 195 } else { 196 PetscCall(PetscContainerGetPointer(container, (void **)&info)); 197 } 198 PetscCall(PetscFree(info->fmt)); 199 PetscCall(PetscStrallocpy(socket->fmt, &info->fmt)); 200 PetscFunctionReturn(0); 201 } 202 203 static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view) { 204 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 205 PetscViewer window = NULL; 206 PetscBool ldis, dis; 207 208 PetscFunctionBegin; 209 PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window)); 210 /* if we could not estabilish a connection, we disable the socket viewer on all MPI ranks */ 211 ldis = !viewer ? PETSC_TRUE : PETSC_FALSE; 212 PetscCall(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer))); 213 if (dis) { 214 socket->status = PETSCVIEWERGLVIS_DISABLED; 215 PetscCall(PetscViewerDestroy(&window)); 216 } 217 *view = window; 218 PetscFunctionReturn(0); 219 } 220 221 PetscErrorCode PetscViewerGLVisPause_Private(PetscViewer viewer) { 222 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 223 224 PetscFunctionBegin; 225 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause)); 226 PetscFunctionReturn(0); 227 } 228 229 /* DM specific support */ 230 PetscErrorCode PetscViewerGLVisSetDM_Private(PetscViewer viewer, PetscObject dm) { 231 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 232 233 PetscFunctionBegin; 234 PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer"); 235 if (!socket->dm) { 236 PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL; 237 238 PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm)); 239 if (setupwithdm) { 240 PetscCall((*setupwithdm)(dm, viewer)); 241 } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name); 242 PetscCall(PetscObjectReference(dm)); 243 socket->dm = dm; 244 } 245 PetscFunctionReturn(0); 246 } 247 248 PetscErrorCode PetscViewerGLVisGetDMWindow_Private(PetscViewer viewer, PetscViewer *view) { 249 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 250 251 PetscFunctionBegin; 252 PetscValidPointer(view, 2); 253 if (!socket->meshwindow) { 254 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) { 255 PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow)); 256 } else { 257 size_t len; 258 PetscBool isstdout; 259 260 PetscCall(PetscStrlen(socket->name, &len)); 261 PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); 262 if (!socket->name || !len || isstdout) { 263 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow)); 264 } else { 265 PetscMPIInt rank; 266 char filename[PETSC_MAX_PATH_LEN]; 267 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); 268 PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank)); 269 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow)); 270 } 271 } 272 if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS)); 273 } 274 if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow)); 275 *view = socket->meshwindow; 276 PetscFunctionReturn(0); 277 } 278 279 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer, PetscViewer *view) { 280 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 281 282 PetscFunctionBegin; 283 PetscValidPointer(view, 2); 284 PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()"); 285 if (*view) { 286 PetscCall(PetscViewerFlush(*view)); 287 PetscCall(PetscBarrier((PetscObject)viewer)); 288 } 289 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ 290 PetscCall(PetscViewerDestroy(&socket->meshwindow)); 291 } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ 292 socket->meshwindow = NULL; 293 } 294 *view = NULL; 295 PetscFunctionReturn(0); 296 } 297 298 PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer, PetscViewerGLVisType *type) { 299 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 300 301 PetscFunctionBegin; 302 PetscValidPointer(type, 2); 303 *type = socket->type; 304 PetscFunctionReturn(0); 305 } 306 307 /* 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 */ 308 PetscErrorCode PetscViewerGLVisGetStatus_Private(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus) { 309 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 310 311 PetscFunctionBegin; 312 PetscValidPointer(sockstatus, 2); 313 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { 314 socket->status = PETSCVIEWERGLVIS_DISCONNECTED; 315 } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) { 316 PetscInt i; 317 PetscBool lconn, conn; 318 319 for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++) 320 if (!socket->window[i]) lconn = PETSC_FALSE; 321 322 PetscCall(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer))); 323 if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED; 324 } 325 *sockstatus = socket->status; 326 PetscFunctionReturn(0); 327 } 328 329 PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject *dm) { 330 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 331 332 PetscFunctionBegin; 333 *dm = socket->dm; 334 PetscFunctionReturn(0); 335 } 336 337 PetscErrorCode PetscViewerGLVisGetFields_Private(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx) { 338 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 339 340 PetscFunctionBegin; 341 if (nfield) *nfield = socket->nwindow; 342 if (fec) *fec = (const char **)socket->fec_type; 343 if (spacedim) *spacedim = socket->spacedim; 344 if (g2lfield) *g2lfield = socket->g2lfield; 345 if (Ufield) *Ufield = socket->Ufield; 346 if (userctx) *userctx = socket->userctx; 347 PetscFunctionReturn(0); 348 } 349 350 /* accessor routines for the viewer windows: 351 PETSC_VIEWER_GLVIS_DUMP : it returns a new viewer every time 352 PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done. 353 */ 354 PetscErrorCode PetscViewerGLVisGetWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) { 355 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 356 PetscViewerGLVisStatus status; 357 358 PetscFunctionBegin; 359 PetscValidLogicalCollectiveInt(viewer, wid, 2); 360 PetscValidPointer(view, 3); 361 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); 362 status = socket->status; 363 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); 364 switch (status) { 365 case PETSCVIEWERGLVIS_DISCONNECTED: 366 PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen"); 367 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { 368 size_t len; 369 PetscBool isstdout; 370 371 PetscCall(PetscStrlen(socket->name, &len)); 372 PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); 373 if (!socket->name || !len || isstdout) { 374 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid])); 375 } else { 376 PetscMPIInt rank; 377 char filename[PETSC_MAX_PATH_LEN]; 378 379 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); 380 PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank)); 381 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid])); 382 } 383 } else { 384 PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid])); 385 } 386 if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS)); 387 *view = socket->window[wid]; 388 break; 389 case PETSCVIEWERGLVIS_CONNECTED: *view = socket->window[wid]; break; 390 case PETSCVIEWERGLVIS_DISABLED: *view = NULL; break; 391 default: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status); 392 } 393 if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view)); 394 PetscFunctionReturn(0); 395 } 396 397 /* Restore the window viewer 398 PETSC_VIEWER_GLVIS_DUMP : destroys the temporary created ASCII viewer used for dumping 399 PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer. 400 - it the returned window viewer is NULL, assumes something went wrong 401 with the socket (i.e. SIGPIPE when a user closes the popup window) 402 and that the caller already handled it (see VecView_GLVis). 403 */ 404 PetscErrorCode PetscViewerGLVisRestoreWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) { 405 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 406 407 PetscFunctionBegin; 408 PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS); 409 PetscValidLogicalCollectiveInt(viewer, wid, 2); 410 PetscValidPointer(view, 3); 411 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); 412 PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()"); 413 if (*view) { 414 PetscCall(PetscViewerFlush(*view)); 415 PetscCall(PetscBarrier((PetscObject)viewer)); 416 } 417 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ 418 PetscCall(PetscViewerDestroy(&socket->window[wid])); 419 } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ 420 socket->window[wid] = NULL; 421 } 422 *view = NULL; 423 PetscFunctionReturn(0); 424 } 425 426 /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */ 427 PetscErrorCode PetscViewerGLVisInitWindow_Private(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name) { 428 PetscViewerGLVisInfo info; 429 PetscContainer container; 430 431 PetscFunctionBegin; 432 PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container)); 433 PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private"); 434 PetscCall(PetscContainerGetPointer(container, (void **)&info)); 435 if (info->init) PetscFunctionReturn(0); 436 437 /* Configure window */ 438 if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1])); 439 if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name)); 440 441 /* Configure default view */ 442 if (mesh) { 443 switch (dim) { 444 case 1: 445 PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ 446 break; 447 case 2: 448 PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ 449 break; 450 case 3: /* TODO: decide default view in 3D */ break; 451 } 452 } else { 453 PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */ 454 switch (dim) { 455 case 1: 456 PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */ 457 break; 458 case 2: 459 PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */ 460 break; 461 case 3: break; 462 } 463 PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */ 464 } 465 466 { /* Additional keys and commands */ 467 char keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = ""; 468 PetscOptions opt = ((PetscObject)viewer)->options; 469 const char *pre = ((PetscObject)viewer)->prefix; 470 471 PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL)); 472 PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL)); 473 if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys)); 474 if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds)); 475 } 476 477 /* Pause visualization */ 478 if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n")); 479 if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n")); 480 481 info->init = PETSC_TRUE; 482 PetscFunctionReturn(0); 483 } 484 485 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer) { 486 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 487 PetscInt i; 488 489 PetscFunctionBegin; 490 for (i = 0; i < socket->nwindow; i++) { 491 PetscCall(PetscViewerDestroy(&socket->window[i])); 492 PetscCall(PetscFree(socket->windowtitle[i])); 493 PetscCall(PetscFree(socket->fec_type[i])); 494 PetscCall(PetscObjectDestroy(&socket->Ufield[i])); 495 } 496 PetscCall(PetscFree(socket->name)); 497 PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield)); 498 PetscCall(PetscFree(socket->fmt)); 499 PetscCall(PetscViewerDestroy(&socket->meshwindow)); 500 PetscCall(PetscObjectDestroy(&socket->dm)); 501 if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); 502 503 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL)); 504 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL)); 505 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL)); 506 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL)); 507 PetscCall(PetscFree(socket)); 508 viewer->data = NULL; 509 PetscFunctionReturn(0); 510 } 511 512 static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject) { 513 PetscViewerGLVis socket = (PetscViewerGLVis)v->data; 514 PetscInt nsizes = 2, prec = PETSC_DECIDE; 515 PetscBool set; 516 517 PetscFunctionBegin; 518 PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options"); 519 PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set)); 520 if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec)); 521 PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set)); 522 if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0]; 523 PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL)); 524 PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, NULL)); 525 PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, NULL)); 526 PetscOptionsHeadEnd(); 527 PetscFunctionReturn(0); 528 } 529 530 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) { 531 char *sport; 532 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 533 534 PetscFunctionBegin; 535 socket->type = PETSC_VIEWER_GLVIS_DUMP; 536 /* we accept localhost^port */ 537 PetscCall(PetscFree(socket->name)); 538 PetscCall(PetscStrallocpy(name, &socket->name)); 539 PetscCall(PetscStrchr(socket->name, '^', &sport)); 540 if (sport) { 541 PetscInt port = 19916; 542 size_t len; 543 PetscErrorCode ierr; 544 545 *sport++ = 0; 546 PetscCall(PetscStrlen(sport, &len)); 547 ierr = PetscOptionsStringToInt(sport, &port); 548 if (PetscUnlikely(ierr)) { 549 socket->port = 19916; 550 } else { 551 socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916; 552 } 553 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 554 } 555 PetscFunctionReturn(0); 556 } 557 558 /*@C 559 PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer` 560 561 Collective 562 563 Input Parameters: 564 + comm - the MPI communicator 565 . type - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file 566 . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations 567 - port - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP` 568 569 Output Parameters: 570 - viewer - the `PetscViewer` object 571 572 Options Database Keys: 573 + -glvis_precision <precision> - Sets number of digits for floating point values 574 . -glvis_size <width,height> - Sets the window size (in pixels) 575 . -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization 576 (0 is default, -1 implies every visualization) 577 . -glvis_keys - Additional keys to configure visualization 578 - -glvis_exec - Additional commands to configure visualization 579 580 Fortran Note: 581 Missing Fortran binding 582 583 Level: beginner 584 585 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType` 586 @*/ 587 PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer) { 588 PetscViewerGLVis socket; 589 590 PetscFunctionBegin; 591 PetscCall(PetscViewerCreate(comm, viewer)); 592 PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS)); 593 594 socket = (PetscViewerGLVis)((*viewer)->data); 595 socket->type = type; 596 if (type == PETSC_VIEWER_GLVIS_DUMP || name) { 597 PetscCall(PetscFree(socket->name)); 598 PetscCall(PetscStrallocpy(name, &socket->name)); 599 } 600 socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port; 601 602 PetscCall(PetscViewerSetFromOptions(*viewer)); 603 PetscFunctionReturn(0); 604 } 605 606 /* 607 PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator. 608 609 Collective 610 611 Input Parameter: 612 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer` 613 614 Level: intermediate 615 616 Environmental variables: 617 + `PETSC_VIEWER_GLVIS_FILENAME` : output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`) 618 . `PETSC_VIEWER_GLVIS_HOSTNAME` : machine where the GLVis server is listening (defaults to localhost) 619 - `PETSC_VIEWER_GLVIS_PORT` : port opened by the GLVis server (defaults to 19916) 620 621 Note: 622 Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return 623 an error code. It is usually used in the form 624 $ XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm)); 625 626 Fortran Note: 627 Missing Fortran bindings 628 629 .seealso: `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()` 630 */ 631 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) { 632 PetscErrorCode ierr; 633 PetscBool flg; 634 PetscViewer viewer; 635 PetscViewerGLVisType type; 636 char fname[PETSC_MAX_PATH_LEN], sport[16]; 637 PetscInt port = 19916; /* default for GLVis */ 638 639 PetscFunctionBegin; 640 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg); 641 if (ierr) { 642 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 643 PetscFunctionReturn(NULL); 644 } 645 if (!flg) { 646 type = PETSC_VIEWER_GLVIS_SOCKET; 647 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg); 648 if (ierr) { 649 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 650 PetscFunctionReturn(NULL); 651 } 652 if (!flg) { 653 ierr = PetscStrcpy(fname, "localhost"); 654 if (ierr) { 655 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 656 PetscFunctionReturn(NULL); 657 } 658 } 659 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg); 660 if (ierr) { 661 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 662 PetscFunctionReturn(NULL); 663 } 664 if (flg) { 665 ierr = PetscOptionsStringToInt(sport, &port); 666 if (ierr) { 667 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 668 PetscFunctionReturn(NULL); 669 } 670 } 671 } else { 672 type = PETSC_VIEWER_GLVIS_DUMP; 673 } 674 ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer); 675 if (ierr) { 676 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 677 PetscFunctionReturn(NULL); 678 } 679 ierr = PetscObjectRegisterDestroy((PetscObject)viewer); 680 if (ierr) { 681 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 682 PetscFunctionReturn(NULL); 683 } 684 PetscFunctionReturn(viewer); 685 } 686 687 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) { 688 PetscViewerGLVis socket; 689 690 PetscFunctionBegin; 691 PetscCall(PetscNewLog(viewer, &socket)); 692 693 /* defaults to socket viewer */ 694 PetscCall(PetscStrallocpy("localhost", &socket->name)); 695 socket->port = 19916; /* GLVis default listening port */ 696 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 697 socket->pause = 0; /* just pause the first time */ 698 699 socket->windowsizes[0] = 600; 700 socket->windowsizes[1] = 600; 701 702 /* defaults to full precision */ 703 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 704 705 viewer->data = (void *)socket; 706 viewer->ops->destroy = PetscViewerDestroy_GLVis; 707 viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; 708 709 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis)); 710 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis)); 711 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis)); 712 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis)); 713 PetscFunctionReturn(0); 714 } 715 716 /* this is a private implementation of a SOCKET with ASCII data format 717 GLVis does not currently handle binary socket streams */ 718 #if defined(PETSC_HAVE_UNISTD_H) 719 #include <unistd.h> 720 #endif 721 722 #if !defined(PETSC_HAVE_WINDOWS_H) 723 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); 724 725 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) { 726 FILE *stream; 727 728 PetscFunctionBegin; 729 PetscCall(PetscViewerASCIIGetPointer(viewer, &stream)); 730 if (stream) { 731 int retv = fclose(stream); 732 PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream"); 733 } 734 PetscCall(PetscViewerDestroy_ASCII(viewer)); 735 PetscFunctionReturn(0); 736 } 737 #endif 738 739 /* 740 This attempts to return a NULL viewer if it is unable to open a socket connection. 741 742 The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 743 744 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value 745 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 746 */ 747 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) { 748 #if defined(PETSC_HAVE_WINDOWS_H) 749 PetscFunctionBegin; 750 SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows"); 751 #else 752 FILE *stream = NULL; 753 int fd = 0; 754 PetscErrorCode ierr; 755 756 PetscFunctionBegin; 757 PetscValidCharPointer(hostname, 2); 758 PetscValidPointer(viewer, 4); 759 #if defined(PETSC_USE_SOCKET_VIEWER) 760 ierr = PetscOpenSocket(hostname, port, &fd); 761 #else 762 SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer"); 763 #endif 764 /* 765 The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 766 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value 767 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 768 */ 769 if (PetscUnlikely(ierr)) { 770 char err[1024]; 771 772 PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port)); 773 PetscCall(PetscInfo(NULL, "%s", err)); 774 *viewer = NULL; 775 PetscFunctionReturn(0); 776 } else { 777 char msg[1024]; 778 779 PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port)); 780 PetscCall(PetscInfo(NULL, "%s", msg)); 781 } 782 stream = fdopen(fd, "w"); /* Not possible on Windows */ 783 PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port); 784 PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer)); 785 PetscViewerDestroy_ASCII = (*viewer)->ops->destroy; 786 (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket; 787 #endif 788 PetscFunctionReturn(0); 789 } 790 791 #if !defined(PETSC_MISSING_SIGPIPE) 792 793 #include <signal.h> 794 795 #if defined(PETSC_HAVE_WINDOWS_H) 796 #define PETSC_DEVNULL "NUL" 797 #else 798 #define PETSC_DEVNULL "/dev/null" 799 #endif 800 801 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; 802 803 static void (*PetscGLVisSigHandler_save)(int) = NULL; 804 805 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) { 806 PetscGLVisBrokenPipe = PETSC_TRUE; 807 #if !defined(PETSC_MISSING_SIG_IGN) 808 signal(SIGPIPE, SIG_IGN); 809 #endif 810 } 811 812 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { 813 PetscFunctionBegin; 814 PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME); 815 PetscGLVisBrokenPipe = PETSC_FALSE; 816 PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE); 817 PetscFunctionReturn(0); 818 } 819 820 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) { 821 PetscBool flag, brokenpipe; 822 823 PetscFunctionBegin; 824 flag = PetscGLVisBrokenPipe; 825 PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm)); 826 if (brokenpipe) { 827 FILE *sock, *null = fopen(PETSC_DEVNULL, "w"); 828 PetscCall(PetscViewerASCIIGetPointer(*win, &sock)); 829 PetscCall(PetscViewerASCIISetFILE(*win, null)); 830 PetscCall(PetscViewerDestroy(win)); 831 if (sock) (void)fclose(sock); 832 } 833 (void)signal(SIGPIPE, PetscGLVisSigHandler_save); 834 PetscGLVisSigHandler_save = NULL; 835 PetscGLVisBrokenPipe = PETSC_FALSE; 836 PetscFunctionReturn(0); 837 } 838 839 #else 840 841 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { 842 PetscFunctionBegin; 843 PetscFunctionReturn(0); 844 } 845 846 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { 847 PetscFunctionBegin; 848 PetscFunctionReturn(0); 849 } 850 851 #endif 852