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 *ctx; /* User context, used by g2lfield */ 28 PetscCtxDestroyFn *destroyctx; /* destroy routine for ctx */ 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: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 45 @*/ 46 PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec) 47 { 48 PetscFunctionBegin; 49 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 50 PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec)); 51 PetscFunctionReturn(PETSC_SUCCESS); 52 } 53 54 static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec) 55 { 56 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 57 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 } 68 69 /*@ 70 PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the `PetscViewerGLVisType` is `PETSC_VIEWER_GLVIS_DUMP` 71 72 Logically Collective 73 74 Input Parameters: 75 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` 76 - id - the current snapshot id in a time-dependent simulation 77 78 Level: beginner 79 80 .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 81 @*/ 82 PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id) 83 { 84 PetscFunctionBegin; 85 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 86 PetscValidLogicalCollectiveInt(viewer, id, 2); 87 PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id)); 88 PetscFunctionReturn(PETSC_SUCCESS); 89 } 90 91 static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id) 92 { 93 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 94 95 PetscFunctionBegin; 96 socket->snapid = id; 97 PetscFunctionReturn(PETSC_SUCCESS); 98 } 99 100 /*@C 101 PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector. 102 103 Logically Collective 104 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 ctx 114 115 Level: intermediate 116 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 122 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. 125 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[], PetscCtx ctx, PetscCtxDestroyFn *destroyctx) 129 { 130 PetscFunctionBegin; 131 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 132 PetscValidLogicalCollectiveInt(viewer, nf, 2); 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 *, PetscCtxDestroyFn *), (viewer, nf, fec_type, dim, g2l, Vfield, ctx, destroyctx)); 138 PetscFunctionReturn(PETSC_SUCCESS); 139 } 140 141 static PetscErrorCode PetscViewerGLVisSetFields_GLVis(PetscViewer viewer, PetscInt nfields, const char *fec_type[], PetscInt dim[], PetscErrorCode (*g2l)(PetscObject, PetscInt, PetscObject[], void *), PetscObject Vfield[], PetscCtx ctx, PetscCtxDestroyFn *destroyctx) 142 { 143 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 144 PetscInt i; 145 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; 150 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; 154 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->ctx) PetscCall((*socket->destroyctx)(&socket->ctx)); 167 socket->ctx = ctx; 168 socket->destroyctx = destroyctx; 169 PetscFunctionReturn(PETSC_SUCCESS); 170 } 171 172 static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(PetscCtxRt ptr) 173 { 174 PetscViewerGLVisInfo info = (PetscViewerGLVisInfo) * (void **)ptr; 175 176 PetscFunctionBegin; 177 PetscCall(PetscFree(info->fmt)); 178 PetscCall(PetscFree(info)); 179 PetscFunctionReturn(PETSC_SUCCESS); 180 } 181 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; 188 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, &info)); 201 } 202 PetscCall(PetscFree(info->fmt)); 203 PetscCall(PetscStrallocpy(socket->fmt, &info->fmt)); 204 PetscFunctionReturn(PETSC_SUCCESS); 205 } 206 207 static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view) 208 { 209 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 210 PetscViewer window = NULL; 211 PetscBool ldis, dis; 212 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 } 225 226 PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer) 227 { 228 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 229 230 PetscFunctionBegin; 231 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause)); 232 PetscFunctionReturn(PETSC_SUCCESS); 233 } 234 235 /* DM specific support */ 236 PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm) 237 { 238 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 239 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; 244 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 } 253 254 PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view) 255 { 256 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 257 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; 266 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 } 285 286 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view) 287 { 288 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 289 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 } 305 306 PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type) 307 { 308 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 309 310 PetscFunctionBegin; 311 PetscAssertPointer(type, 2); 312 *type = socket->type; 313 PetscFunctionReturn(PETSC_SUCCESS); 314 } 315 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; 320 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; 328 329 for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++) 330 if (!socket->window[i]) lconn = PETSC_FALSE; 331 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 } 338 339 PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm) 340 { 341 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 342 343 PetscFunctionBegin; 344 *dm = socket->dm; 345 PetscFunctionReturn(PETSC_SUCCESS); 346 } 347 348 PetscErrorCode PetscViewerGLVisGetFields_Internal(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **ctx) 349 { 350 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 351 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 (ctx) *ctx = socket->ctx; 359 PetscFunctionReturn(PETSC_SUCCESS); 360 } 361 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; 370 371 PetscFunctionBegin; 372 PetscValidLogicalCollectiveInt(viewer, wid, 2); 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; 383 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]; 391 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 } 414 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; 425 426 PetscFunctionBegin; 427 PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS); 428 PetscValidLogicalCollectiveInt(viewer, wid, 2); 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 } 444 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; 450 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, &info)); 455 if (info->init) PetscFunctionReturn(PETSC_SUCCESS); 456 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)); 460 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 } 487 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; 492 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 } 498 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")); 502 503 info->init = PETSC_TRUE; 504 PetscFunctionReturn(PETSC_SUCCESS); 505 } 506 507 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer) 508 { 509 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 510 PetscInt i; 511 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->ctx) PetscCall((*socket->destroyctx)(&socket->ctx)); 525 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 } 534 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; 540 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 } 553 554 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) 555 { 556 char *sport = NULL; 557 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 558 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; 569 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 } 582 583 /*@ 584 PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer` 585 586 Collective; No Fortran Support 587 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` 593 594 Output Parameter: 595 . viewer - the `PetscViewer` object 596 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 604 605 Level: beginner 606 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; 612 613 PetscFunctionBegin; 614 PetscCall(PetscViewerCreate(comm, viewer)); 615 PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS)); 616 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; 624 625 PetscCall(PetscViewerSetFromOptions(*viewer)); 626 PetscFunctionReturn(PETSC_SUCCESS); 627 } 628 629 /*@C 630 PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator. 631 632 Collective; No Fortran Support 633 634 Input Parameter: 635 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer` 636 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) 641 642 Level: intermediate 643 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 650 651 Developer Note: 652 How come this viewer is not stashed as an attribute in the MPI communicator? 653 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 */ 663 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 } 679 680 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) 681 { 682 PetscViewerGLVis socket; 683 684 PetscFunctionBegin; 685 PetscCall(PetscNew(&socket)); 686 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 */ 692 693 socket->windowsizes[0] = 600; 694 socket->windowsizes[1] = 600; 695 696 /* defaults to full precision */ 697 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 698 699 viewer->data = (void *)socket; 700 viewer->ops->destroy = PetscViewerDestroy_GLVis; 701 viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; 702 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 } 709 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 715 716 #ifndef PETSC_HAVE_WINDOWS_H 717 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); 718 719 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) 720 { 721 FILE *stream; 722 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 733 734 /* 735 This attempts to return a NULL viewer if it is unable to open a socket connection. 736 737 The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 738 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; 752 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 } 782 783 #if !defined(PETSC_MISSING_SIGPIPE) 784 785 #include <signal.h> 786 787 #if defined(PETSC_HAVE_WINDOWS_H) 788 #define PETSC_DEVNULL "NUL" 789 #else 790 #define PETSC_DEVNULL "/dev/null" 791 #endif 792 793 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; 794 795 static void (*PetscGLVisSigHandler_save)(int) = NULL; 796 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 } 804 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 } 813 814 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) 815 { 816 PetscBool flag, brokenpipe; 817 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 } 833 834 #else 835 836 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 837 { 838 PetscFunctionBegin; 839 PetscFunctionReturn(PETSC_SUCCESS); 840 } 841 842 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 843 { 844 PetscFunctionBegin; 845 PetscFunctionReturn(PETSC_SUCCESS); 846 } 847 848 #endif 849