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(0); 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(0); 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(0); 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(0); 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 . g2lfields - 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 Notes: 116 g2lfields is called on the vector V to be visualized in order to extract the relevant dofs to be put in Vfield[], as 117 .vb 118 g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx). 119 .ve 120 121 For vector spaces, the block size of Vfield[i] represents the vector dimension. 122 The names of the Vfield vectors will be displayed in the window title. 123 124 Level: intermediate 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 PetscValidPointer(fec_type, 3); 135 PetscValidIntPointer(dim, 4); 136 PetscValidPointer(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(0); 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(0); 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(0); 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(0); 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 estabilish 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(0); 228 } 229 230 PetscErrorCode PetscViewerGLVisPause_Private(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(0); 237 } 238 239 /* DM specific support */ 240 PetscErrorCode PetscViewerGLVisSetDM_Private(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(0); 257 } 258 259 PetscErrorCode PetscViewerGLVisGetDMWindow_Private(PetscViewer viewer, PetscViewer *view) 260 { 261 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 262 263 PetscFunctionBegin; 264 PetscValidPointer(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(0); 289 } 290 291 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer, PetscViewer *view) 292 { 293 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 294 295 PetscFunctionBegin; 296 PetscValidPointer(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(0); 309 } 310 311 PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer, PetscViewerGLVisType *type) 312 { 313 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 314 315 PetscFunctionBegin; 316 PetscValidPointer(type, 2); 317 *type = socket->type; 318 PetscFunctionReturn(0); 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_Private(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus) 323 { 324 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 325 326 PetscFunctionBegin; 327 PetscValidPointer(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(0); 342 } 343 344 PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject *dm) 345 { 346 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 347 348 PetscFunctionBegin; 349 *dm = socket->dm; 350 PetscFunctionReturn(0); 351 } 352 353 PetscErrorCode PetscViewerGLVisGetFields_Private(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(0); 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_Private(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 PetscValidPointer(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(0); 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_Private(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 PetscValidPointer(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(0); 448 } 449 450 /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */ 451 PetscErrorCode PetscViewerGLVisInitWindow_Private(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(0); 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(0); 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(0); 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, NULL)); 554 PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, NULL)); 555 PetscOptionsHeadEnd(); 556 PetscFunctionReturn(0); 557 } 558 559 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) 560 { 561 char *sport; 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(0); 586 } 587 588 /*@C 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 Parameters: 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(0); 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 $ XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm)); 653 654 .seealso: [](sec_viewers), `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()` 655 @*/ 656 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) 657 { 658 PetscErrorCode ierr; 659 PetscBool flg; 660 PetscViewer viewer; 661 PetscViewerGLVisType type; 662 char fname[PETSC_MAX_PATH_LEN], sport[16]; 663 PetscInt port = 19916; /* default for GLVis */ 664 665 PetscFunctionBegin; 666 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg); 667 if (ierr) { 668 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 669 PetscFunctionReturn(NULL); 670 } 671 if (!flg) { 672 type = PETSC_VIEWER_GLVIS_SOCKET; 673 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg); 674 if (ierr) { 675 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 676 PetscFunctionReturn(NULL); 677 } 678 if (!flg) { 679 ierr = PetscStrcpy(fname, "localhost"); 680 if (ierr) { 681 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 682 PetscFunctionReturn(NULL); 683 } 684 } 685 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg); 686 if (ierr) { 687 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 688 PetscFunctionReturn(NULL); 689 } 690 if (flg) { 691 ierr = PetscOptionsStringToInt(sport, &port); 692 if (ierr) { 693 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 694 PetscFunctionReturn(NULL); 695 } 696 } 697 } else { 698 type = PETSC_VIEWER_GLVIS_DUMP; 699 } 700 ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer); 701 if (ierr) { 702 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 703 PetscFunctionReturn(NULL); 704 } 705 ierr = PetscObjectRegisterDestroy((PetscObject)viewer); 706 if (ierr) { 707 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 708 PetscFunctionReturn(NULL); 709 } 710 PetscFunctionReturn(viewer); 711 } 712 713 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) 714 { 715 PetscViewerGLVis socket; 716 717 PetscFunctionBegin; 718 PetscCall(PetscNew(&socket)); 719 720 /* defaults to socket viewer */ 721 PetscCall(PetscStrallocpy("localhost", &socket->name)); 722 socket->port = 19916; /* GLVis default listening port */ 723 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 724 socket->pause = 0; /* just pause the first time */ 725 726 socket->windowsizes[0] = 600; 727 socket->windowsizes[1] = 600; 728 729 /* defaults to full precision */ 730 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 731 732 viewer->data = (void *)socket; 733 viewer->ops->destroy = PetscViewerDestroy_GLVis; 734 viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; 735 736 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis)); 737 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis)); 738 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis)); 739 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis)); 740 PetscFunctionReturn(0); 741 } 742 743 /* this is a private implementation of a SOCKET with ASCII data format 744 GLVis does not currently handle binary socket streams */ 745 #if defined(PETSC_HAVE_UNISTD_H) 746 #include <unistd.h> 747 #endif 748 749 #ifndef PETSC_HAVE_WINDOWS_H 750 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); 751 752 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) 753 { 754 FILE *stream; 755 756 PetscFunctionBegin; 757 PetscCall(PetscViewerASCIIGetPointer(viewer, &stream)); 758 if (stream) { 759 int retv = fclose(stream); 760 PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream"); 761 } 762 PetscCall(PetscViewerDestroy_ASCII(viewer)); 763 PetscFunctionReturn(0); 764 } 765 #endif 766 767 /* 768 This attempts to return a NULL viewer if it is unable to open a socket connection. 769 770 The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 771 772 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value 773 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 774 */ 775 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) 776 { 777 #if defined(PETSC_HAVE_WINDOWS_H) 778 PetscFunctionBegin; 779 SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows"); 780 #else 781 FILE *stream = NULL; 782 int fd = 0; 783 PetscErrorCode ierr; 784 785 PetscFunctionBegin; 786 PetscValidCharPointer(hostname, 2); 787 PetscValidPointer(viewer, 4); 788 #if defined(PETSC_USE_SOCKET_VIEWER) 789 ierr = PetscOpenSocket(hostname, port, &fd); 790 #else 791 SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer"); 792 #endif 793 /* 794 The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 795 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value 796 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 797 */ 798 if (PetscUnlikely(ierr)) { 799 char err[1024]; 800 801 PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port)); 802 PetscCall(PetscInfo(NULL, "%s", err)); 803 *viewer = NULL; 804 PetscFunctionReturn(0); 805 } else { 806 char msg[1024]; 807 808 PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port)); 809 PetscCall(PetscInfo(NULL, "%s", msg)); 810 } 811 stream = fdopen(fd, "w"); /* Not possible on Windows */ 812 PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port); 813 PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer)); 814 PetscViewerDestroy_ASCII = (*viewer)->ops->destroy; 815 (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket; 816 #endif 817 PetscFunctionReturn(0); 818 } 819 820 #if !defined(PETSC_MISSING_SIGPIPE) 821 822 #include <signal.h> 823 824 #if defined(PETSC_HAVE_WINDOWS_H) 825 #define PETSC_DEVNULL "NUL" 826 #else 827 #define PETSC_DEVNULL "/dev/null" 828 #endif 829 830 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; 831 832 static void (*PetscGLVisSigHandler_save)(int) = NULL; 833 834 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) 835 { 836 PetscGLVisBrokenPipe = PETSC_TRUE; 837 #if !defined(PETSC_MISSING_SIG_IGN) 838 signal(SIGPIPE, SIG_IGN); 839 #endif 840 } 841 842 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 843 { 844 PetscFunctionBegin; 845 PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME); 846 PetscGLVisBrokenPipe = PETSC_FALSE; 847 PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE); 848 PetscFunctionReturn(0); 849 } 850 851 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) 852 { 853 PetscBool flag, brokenpipe; 854 855 PetscFunctionBegin; 856 flag = PetscGLVisBrokenPipe; 857 PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm)); 858 if (brokenpipe) { 859 FILE *sock, *null = fopen(PETSC_DEVNULL, "w"); 860 PetscCall(PetscViewerASCIIGetPointer(*win, &sock)); 861 PetscCall(PetscViewerASCIISetFILE(*win, null)); 862 PetscCall(PetscViewerDestroy(win)); 863 if (sock) (void)fclose(sock); 864 } 865 (void)signal(SIGPIPE, PetscGLVisSigHandler_save); 866 PetscGLVisSigHandler_save = NULL; 867 PetscGLVisBrokenPipe = PETSC_FALSE; 868 PetscFunctionReturn(0); 869 } 870 871 #else 872 873 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 874 { 875 PetscFunctionBegin; 876 PetscFunctionReturn(0); 877 } 878 879 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 880 { 881 PetscFunctionBegin; 882 PetscFunctionReturn(0); 883 } 884 885 #endif 886