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: [](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 userctx 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[], void *ctx, PetscErrorCode (*destroyctx)(void *)) 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 *, PetscErrorCode (*)(void *)), (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[], void *ctx, PetscErrorCode (*destroyctx)(void *)) 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->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); 167 socket->userctx = ctx; 168 socket->destroyctx = destroyctx; 169 PetscFunctionReturn(PETSC_SUCCESS); 170 } 171 172 static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr) 173 { 174 PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)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(PetscContainerCreate(PetscObjectComm((PetscObject)window), &container)); 199 PetscCall(PetscContainerSetPointer(container, (void *)info)); 200 PetscCall(PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private)); 201 PetscCall(PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container)); 202 PetscCall(PetscContainerDestroy(&container)); 203 } else { 204 PetscCall(PetscContainerGetPointer(container, (void **)&info)); 205 } 206 PetscCall(PetscFree(info->fmt)); 207 PetscCall(PetscStrallocpy(socket->fmt, &info->fmt)); 208 PetscFunctionReturn(PETSC_SUCCESS); 209 } 210 211 static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view) 212 { 213 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 214 PetscViewer window = NULL; 215 PetscBool ldis, dis; 216 217 PetscFunctionBegin; 218 PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window)); 219 /* if we could not establish a connection, we disable the socket viewer on all MPI ranks */ 220 ldis = !viewer ? PETSC_TRUE : PETSC_FALSE; 221 PetscCall(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer))); 222 if (dis) { 223 socket->status = PETSCVIEWERGLVIS_DISABLED; 224 PetscCall(PetscViewerDestroy(&window)); 225 } 226 *view = window; 227 PetscFunctionReturn(PETSC_SUCCESS); 228 } 229 230 PetscErrorCode PetscViewerGLVisPause_Internal(PetscViewer viewer) 231 { 232 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 233 234 PetscFunctionBegin; 235 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause)); 236 PetscFunctionReturn(PETSC_SUCCESS); 237 } 238 239 /* DM specific support */ 240 PetscErrorCode PetscViewerGLVisSetDM_Internal(PetscViewer viewer, PetscObject dm) 241 { 242 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 243 244 PetscFunctionBegin; 245 PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer"); 246 if (!socket->dm) { 247 PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL; 248 249 PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm)); 250 if (setupwithdm) { 251 PetscCall((*setupwithdm)(dm, viewer)); 252 } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name); 253 PetscCall(PetscObjectReference(dm)); 254 socket->dm = dm; 255 } 256 PetscFunctionReturn(PETSC_SUCCESS); 257 } 258 259 PetscErrorCode PetscViewerGLVisGetDMWindow_Internal(PetscViewer viewer, PetscViewer *view) 260 { 261 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 262 263 PetscFunctionBegin; 264 PetscAssertPointer(view, 2); 265 if (!socket->meshwindow) { 266 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) { 267 PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow)); 268 } else { 269 size_t len; 270 PetscBool isstdout; 271 272 PetscCall(PetscStrlen(socket->name, &len)); 273 PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); 274 if (!socket->name || !len || isstdout) { 275 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow)); 276 } else { 277 PetscMPIInt rank; 278 char filename[PETSC_MAX_PATH_LEN]; 279 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); 280 PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank)); 281 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow)); 282 } 283 } 284 if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS)); 285 } 286 if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow)); 287 *view = socket->meshwindow; 288 PetscFunctionReturn(PETSC_SUCCESS); 289 } 290 291 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Internal(PetscViewer viewer, PetscViewer *view) 292 { 293 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 294 295 PetscFunctionBegin; 296 PetscAssertPointer(view, 2); 297 PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()"); 298 if (*view) { 299 PetscCall(PetscViewerFlush(*view)); 300 PetscCall(PetscBarrier((PetscObject)viewer)); 301 } 302 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ 303 PetscCall(PetscViewerDestroy(&socket->meshwindow)); 304 } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ 305 socket->meshwindow = NULL; 306 } 307 *view = NULL; 308 PetscFunctionReturn(PETSC_SUCCESS); 309 } 310 311 PetscErrorCode PetscViewerGLVisGetType_Internal(PetscViewer viewer, PetscViewerGLVisType *type) 312 { 313 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 314 315 PetscFunctionBegin; 316 PetscAssertPointer(type, 2); 317 *type = socket->type; 318 PetscFunctionReturn(PETSC_SUCCESS); 319 } 320 321 /* This function is only relevant in the SOCKET_GLIVS case. The status is computed the first time it is requested, as GLVis currently has issues when connecting the first time through the socket */ 322 PetscErrorCode PetscViewerGLVisGetStatus_Internal(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus) 323 { 324 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 325 326 PetscFunctionBegin; 327 PetscAssertPointer(sockstatus, 2); 328 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { 329 socket->status = PETSCVIEWERGLVIS_DISCONNECTED; 330 } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) { 331 PetscInt i; 332 PetscBool lconn, conn; 333 334 for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++) 335 if (!socket->window[i]) lconn = PETSC_FALSE; 336 337 PetscCall(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer))); 338 if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED; 339 } 340 *sockstatus = socket->status; 341 PetscFunctionReturn(PETSC_SUCCESS); 342 } 343 344 PetscErrorCode PetscViewerGLVisGetDM_Internal(PetscViewer viewer, PetscObject *dm) 345 { 346 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 347 348 PetscFunctionBegin; 349 *dm = socket->dm; 350 PetscFunctionReturn(PETSC_SUCCESS); 351 } 352 353 PetscErrorCode PetscViewerGLVisGetFields_Internal(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx) 354 { 355 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 356 357 PetscFunctionBegin; 358 if (nfield) *nfield = socket->nwindow; 359 if (fec) *fec = (const char **)socket->fec_type; 360 if (spacedim) *spacedim = socket->spacedim; 361 if (g2lfield) *g2lfield = socket->g2lfield; 362 if (Ufield) *Ufield = socket->Ufield; 363 if (userctx) *userctx = socket->userctx; 364 PetscFunctionReturn(PETSC_SUCCESS); 365 } 366 367 /* accessor routines for the viewer windows: 368 PETSC_VIEWER_GLVIS_DUMP : it returns a new viewer every time 369 PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done. 370 */ 371 PetscErrorCode PetscViewerGLVisGetWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view) 372 { 373 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 374 PetscViewerGLVisStatus status; 375 376 PetscFunctionBegin; 377 PetscValidLogicalCollectiveInt(viewer, wid, 2); 378 PetscAssertPointer(view, 3); 379 PetscCheck(wid >= 0 && (wid <= socket->nwindow - 1), PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Cannot get window id %" PetscInt_FMT ": allowed range [0,%" PetscInt_FMT ")", wid, socket->nwindow - 1); 380 status = socket->status; 381 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "Window %" PetscInt_FMT " is already in use", wid); 382 switch (status) { 383 case PETSCVIEWERGLVIS_DISCONNECTED: 384 PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen"); 385 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { 386 size_t len; 387 PetscBool isstdout; 388 389 PetscCall(PetscStrlen(socket->name, &len)); 390 PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); 391 if (!socket->name || !len || isstdout) { 392 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid])); 393 } else { 394 PetscMPIInt rank; 395 char filename[PETSC_MAX_PATH_LEN]; 396 397 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); 398 PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank)); 399 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid])); 400 } 401 } else { 402 PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid])); 403 } 404 if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS)); 405 *view = socket->window[wid]; 406 break; 407 case PETSCVIEWERGLVIS_CONNECTED: 408 *view = socket->window[wid]; 409 break; 410 case PETSCVIEWERGLVIS_DISABLED: 411 *view = NULL; 412 break; 413 default: 414 SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status); 415 } 416 if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view)); 417 PetscFunctionReturn(PETSC_SUCCESS); 418 } 419 420 /* Restore the window viewer 421 PETSC_VIEWER_GLVIS_DUMP : destroys the temporary created ASCII viewer used for dumping 422 PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer. 423 - it the returned window viewer is NULL, assumes something went wrong 424 with the socket (i.e. SIGPIPE when a user closes the popup window) 425 and that the caller already handled it (see VecView_GLVis). 426 */ 427 PetscErrorCode PetscViewerGLVisRestoreWindow_Internal(PetscViewer viewer, PetscInt wid, PetscViewer *view) 428 { 429 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 430 431 PetscFunctionBegin; 432 PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS); 433 PetscValidLogicalCollectiveInt(viewer, wid, 2); 434 PetscAssertPointer(view, 3); 435 PetscCheck(wid >= 0 && wid < socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Cannot restore window id %" PetscInt_FMT ": allowed range [0,%" PetscInt_FMT ")", wid, socket->nwindow); 436 PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()"); 437 if (*view) { 438 PetscCall(PetscViewerFlush(*view)); 439 PetscCall(PetscBarrier((PetscObject)viewer)); 440 } 441 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ 442 PetscCall(PetscViewerDestroy(&socket->window[wid])); 443 } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ 444 socket->window[wid] = NULL; 445 } 446 *view = NULL; 447 PetscFunctionReturn(PETSC_SUCCESS); 448 } 449 450 /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */ 451 PetscErrorCode PetscViewerGLVisInitWindow_Internal(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name) 452 { 453 PetscViewerGLVisInfo info; 454 PetscContainer container; 455 456 PetscFunctionBegin; 457 PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container)); 458 PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private"); 459 PetscCall(PetscContainerGetPointer(container, (void **)&info)); 460 if (info->init) PetscFunctionReturn(PETSC_SUCCESS); 461 462 /* Configure window */ 463 if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1])); 464 if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name)); 465 466 /* Configure default view */ 467 if (mesh) { 468 switch (dim) { 469 case 1: 470 PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ 471 break; 472 case 2: 473 PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ 474 break; 475 case 3: /* TODO: decide default view in 3D */ 476 break; 477 } 478 } else { 479 PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */ 480 switch (dim) { 481 case 1: 482 PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */ 483 break; 484 case 2: 485 PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */ 486 break; 487 case 3: 488 break; 489 } 490 PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */ 491 } 492 493 { /* Additional keys and commands */ 494 char keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = ""; 495 PetscOptions opt = ((PetscObject)viewer)->options; 496 const char *pre = ((PetscObject)viewer)->prefix; 497 498 PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL)); 499 PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL)); 500 if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys)); 501 if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds)); 502 } 503 504 /* Pause visualization */ 505 if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n")); 506 if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n")); 507 508 info->init = PETSC_TRUE; 509 PetscFunctionReturn(PETSC_SUCCESS); 510 } 511 512 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer) 513 { 514 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 515 PetscInt i; 516 517 PetscFunctionBegin; 518 for (i = 0; i < socket->nwindow; i++) { 519 PetscCall(PetscViewerDestroy(&socket->window[i])); 520 PetscCall(PetscFree(socket->windowtitle[i])); 521 PetscCall(PetscFree(socket->fec_type[i])); 522 PetscCall(PetscObjectDestroy(&socket->Ufield[i])); 523 } 524 PetscCall(PetscFree(socket->name)); 525 PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield)); 526 PetscCall(PetscFree(socket->fmt)); 527 PetscCall(PetscViewerDestroy(&socket->meshwindow)); 528 PetscCall(PetscObjectDestroy(&socket->dm)); 529 if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); 530 531 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL)); 532 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL)); 533 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL)); 534 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL)); 535 PetscCall(PetscFree(socket)); 536 viewer->data = NULL; 537 PetscFunctionReturn(PETSC_SUCCESS); 538 } 539 540 static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject) 541 { 542 PetscViewerGLVis socket = (PetscViewerGLVis)v->data; 543 PetscInt nsizes = 2, prec = PETSC_DECIDE; 544 PetscBool set; 545 546 PetscFunctionBegin; 547 PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options"); 548 PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set)); 549 if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec)); 550 PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set)); 551 if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0]; 552 PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL)); 553 PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, &set)); 554 PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, &set)); 555 PetscOptionsHeadEnd(); 556 PetscFunctionReturn(PETSC_SUCCESS); 557 } 558 559 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) 560 { 561 char *sport = NULL; 562 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 563 564 PetscFunctionBegin; 565 socket->type = PETSC_VIEWER_GLVIS_DUMP; 566 /* we accept localhost^port */ 567 PetscCall(PetscFree(socket->name)); 568 PetscCall(PetscStrallocpy(name, &socket->name)); 569 PetscCall(PetscStrchr(socket->name, '^', &sport)); 570 if (sport) { 571 PetscInt port = 19916; 572 size_t len; 573 PetscErrorCode ierr; 574 575 *sport++ = 0; 576 PetscCall(PetscStrlen(sport, &len)); 577 ierr = PetscOptionsStringToInt(sport, &port); 578 if (PetscUnlikely(ierr)) { 579 socket->port = 19916; 580 } else { 581 socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916; 582 } 583 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 584 } 585 PetscFunctionReturn(PETSC_SUCCESS); 586 } 587 588 /*@ 589 PetscViewerGLVisOpen - Opens a `PETSCVIEWERGLVIS` `PetscViewer` 590 591 Collective; No Fortran Support 592 593 Input Parameters: 594 + comm - the MPI communicator 595 . type - the viewer type: `PETSC_VIEWER_GLVIS_SOCKET` for real-time visualization or `PETSC_VIEWER_GLVIS_DUMP` for dumping to a file 596 . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations 597 - port - socket port where the GLVis server is listening. Not referenced when type is `PETSC_VIEWER_GLVIS_DUMP` 598 599 Output Parameter: 600 . viewer - the `PetscViewer` object 601 602 Options Database Keys: 603 + -glvis_precision <precision> - Sets number of digits for floating point values 604 . -glvis_size <width,height> - Sets the window size (in pixels) 605 . -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization 606 (0 is default, -1 implies every visualization) 607 . -glvis_keys - Additional keys to configure visualization 608 - -glvis_exec - Additional commands to configure visualization 609 610 Level: beginner 611 612 .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType` 613 @*/ 614 PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer) 615 { 616 PetscViewerGLVis socket; 617 618 PetscFunctionBegin; 619 PetscCall(PetscViewerCreate(comm, viewer)); 620 PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS)); 621 622 socket = (PetscViewerGLVis)((*viewer)->data); 623 socket->type = type; 624 if (type == PETSC_VIEWER_GLVIS_DUMP || name) { 625 PetscCall(PetscFree(socket->name)); 626 PetscCall(PetscStrallocpy(name, &socket->name)); 627 } 628 socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port; 629 630 PetscCall(PetscViewerSetFromOptions(*viewer)); 631 PetscFunctionReturn(PETSC_SUCCESS); 632 } 633 634 /*@C 635 PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator. 636 637 Collective; No Fortran Support 638 639 Input Parameter: 640 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer` 641 642 Environmental variables: 643 + `PETSC_VIEWER_GLVIS_FILENAME` - output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`) 644 . `PETSC_VIEWER_GLVIS_HOSTNAME` - machine where the GLVis server is listening (defaults to localhost) 645 - `PETSC_VIEWER_GLVIS_PORT` - port opened by the GLVis server (defaults to 19916) 646 647 Level: intermediate 648 649 Note: 650 Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return 651 an error code. It is usually used in the form 652 .vb 653 XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm)); 654 .ve 655 656 Developer Note: 657 How come this viewer is not stashed as an attribute in the MPI communicator? 658 659 .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()` 660 @*/ 661 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) 662 { 663 PetscBool flg; 664 PetscViewer viewer; 665 PetscViewerGLVisType type; 666 char fname[PETSC_MAX_PATH_LEN], sport[16]; 667 PetscInt port = 19916; /* default for GLVis */ 668 669 PetscFunctionBegin; 670 PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg)); 671 if (!flg) { 672 type = PETSC_VIEWER_GLVIS_SOCKET; 673 PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg)); 674 if (!flg) { PetscCallNull(PetscStrncpy(fname, "localhost", sizeof(fname))); } 675 PetscCallNull(PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg)); 676 if (flg) { PetscCallNull(PetscOptionsStringToInt(sport, &port)); } 677 } else { 678 type = PETSC_VIEWER_GLVIS_DUMP; 679 } 680 PetscCallNull(PetscViewerGLVisOpen(comm, type, fname, port, &viewer)); 681 PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer)); 682 PetscFunctionReturn(viewer); 683 } 684 685 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) 686 { 687 PetscViewerGLVis socket; 688 689 PetscFunctionBegin; 690 PetscCall(PetscNew(&socket)); 691 692 /* defaults to socket viewer */ 693 PetscCall(PetscStrallocpy("localhost", &socket->name)); 694 socket->port = 19916; /* GLVis default listening port */ 695 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 696 socket->pause = 0; /* just pause the first time */ 697 698 socket->windowsizes[0] = 600; 699 socket->windowsizes[1] = 600; 700 701 /* defaults to full precision */ 702 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 703 704 viewer->data = (void *)socket; 705 viewer->ops->destroy = PetscViewerDestroy_GLVis; 706 viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; 707 708 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis)); 709 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis)); 710 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis)); 711 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis)); 712 PetscFunctionReturn(PETSC_SUCCESS); 713 } 714 715 /* this is a private implementation of a SOCKET with ASCII data format 716 GLVis does not currently handle binary socket streams */ 717 #if defined(PETSC_HAVE_UNISTD_H) 718 #include <unistd.h> 719 #endif 720 721 #ifndef PETSC_HAVE_WINDOWS_H 722 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); 723 724 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) 725 { 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(PETSC_SUCCESS); 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 { 749 #if defined(PETSC_HAVE_WINDOWS_H) 750 PetscFunctionBegin; 751 SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows"); 752 #else 753 FILE *stream = NULL; 754 int fd = 0; 755 PetscErrorCode ierr; 756 757 PetscFunctionBegin; 758 PetscAssertPointer(hostname, 2); 759 PetscAssertPointer(viewer, 4); 760 #if defined(PETSC_USE_SOCKET_VIEWER) 761 ierr = PetscOpenSocket(hostname, (int)port, &fd); 762 #else 763 SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer"); 764 #endif 765 /* 766 The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 767 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value 768 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 769 */ 770 if (PetscUnlikely(ierr)) { 771 PetscCall(PetscInfo(NULL, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port)); 772 *viewer = NULL; 773 PetscFunctionReturn(PETSC_SUCCESS); 774 } else { 775 PetscCall(PetscInfo(NULL, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port)); 776 } 777 stream = fdopen(fd, "w"); /* Not possible on Windows */ 778 PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port); 779 PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer)); 780 PetscViewerDestroy_ASCII = (*viewer)->ops->destroy; 781 (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket; 782 #endif 783 PetscFunctionReturn(PETSC_SUCCESS); 784 } 785 786 #if !defined(PETSC_MISSING_SIGPIPE) 787 788 #include <signal.h> 789 790 #if defined(PETSC_HAVE_WINDOWS_H) 791 #define PETSC_DEVNULL "NUL" 792 #else 793 #define PETSC_DEVNULL "/dev/null" 794 #endif 795 796 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; 797 798 static void (*PetscGLVisSigHandler_save)(int) = NULL; 799 800 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) 801 { 802 PetscGLVisBrokenPipe = PETSC_TRUE; 803 #if !defined(PETSC_MISSING_SIG_IGN) 804 signal(SIGPIPE, SIG_IGN); 805 #endif 806 } 807 808 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 809 { 810 PetscFunctionBegin; 811 PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME); 812 PetscGLVisBrokenPipe = PETSC_FALSE; 813 PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE); 814 PetscFunctionReturn(PETSC_SUCCESS); 815 } 816 817 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) 818 { 819 PetscBool flag, brokenpipe; 820 821 PetscFunctionBegin; 822 flag = PetscGLVisBrokenPipe; 823 PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm)); 824 if (brokenpipe) { 825 FILE *sock, *null = fopen(PETSC_DEVNULL, "w"); 826 PetscCall(PetscViewerASCIIGetPointer(*win, &sock)); 827 PetscCall(PetscViewerASCIISetFILE(*win, null)); 828 PetscCall(PetscViewerDestroy(win)); 829 if (sock) (void)fclose(sock); 830 } 831 (void)signal(SIGPIPE, PetscGLVisSigHandler_save); 832 PetscGLVisSigHandler_save = NULL; 833 PetscGLVisBrokenPipe = PETSC_FALSE; 834 PetscFunctionReturn(PETSC_SUCCESS); 835 } 836 837 #else 838 839 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 840 { 841 PetscFunctionBegin; 842 PetscFunctionReturn(PETSC_SUCCESS); 843 } 844 845 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 846 { 847 PetscFunctionBegin; 848 PetscFunctionReturn(PETSC_SUCCESS); 849 } 850 851 #endif 852