1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for fdopen() */ 2 3 #include <petsc/private/viewerimpl.h> /*I "petscviewer.h" I*/ 4 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/ 5 #include <petsc/private/glvisviewerimpl.h> 6 7 /* we may eventually make this function public */ 8 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm, const char *, PetscInt, PetscViewer *); 9 10 struct _n_PetscViewerGLVis { 11 PetscViewerGLVisStatus status; 12 PetscViewerGLVisType type; /* either PETSC_VIEWER_GLVIS_DUMP or PETSC_VIEWER_GLVIS_SOCKET */ 13 char *name; /* prefix for filename, or hostname, depending on the type */ 14 PetscInt port; /* used just for the socket case */ 15 PetscReal pause; /* if positive, calls PetscSleep(pause) after each VecView_GLVis call */ 16 PetscViewer meshwindow; /* used just by the ASCII dumping */ 17 PetscObject dm; /* DM as passed by PetscViewerGLVisSetDM_Private(): should contain discretization info */ 18 PetscInt nwindow; /* number of windows/fields to be visualized */ 19 PetscViewer *window; 20 char **windowtitle; 21 PetscInt windowsizes[2]; 22 char **fec_type; /* type of elements to be used for visualization, see FiniteElementCollection::Name() */ 23 PetscErrorCode (*g2lfield)(PetscObject, PetscInt, PetscObject[], void *); /* global to local operation for generating dofs to be visualized */ 24 PetscInt *spacedim; /* geometrical space dimension (just used to initialize the scene) */ 25 PetscObject *Ufield; /* work vectors for visualization */ 26 PetscInt snapid; /* snapshot id, use PetscViewerGLVisSetSnapId to change this value*/ 27 void *userctx; /* User context, used by g2lfield */ 28 PetscErrorCode (*destroyctx)(void *); /* destroy routine for userctx */ 29 char *fmt; /* format string for FP values */ 30 }; 31 typedef struct _n_PetscViewerGLVis *PetscViewerGLVis; 32 33 /*@ 34 PetscViewerGLVisSetPrecision - Set the number of digits for floating point values to be displayed 35 36 Not Collective 37 38 Input Parameters: 39 + viewer - the `PetscViewer` of type `PETSCVIEWERGLVIS` 40 - prec - the number of digits required 41 42 Level: beginner 43 44 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 45 @*/ 46 PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec) 47 { 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 on viewer 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: `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 on viewer 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. It misses the Fortran bindings. 122 The names of the Vfield vectors will be displayed in the window title. 123 124 Level: intermediate 125 126 .seealso: `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 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 Fortran Note: 611 Missing Fortran binding 612 613 Level: beginner 614 615 .seealso: `PETSCVIEWERGLVIS`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType` 616 @*/ 617 PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer) 618 { 619 PetscViewerGLVis socket; 620 621 PetscFunctionBegin; 622 PetscCall(PetscViewerCreate(comm, viewer)); 623 PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS)); 624 625 socket = (PetscViewerGLVis)((*viewer)->data); 626 socket->type = type; 627 if (type == PETSC_VIEWER_GLVIS_DUMP || name) { 628 PetscCall(PetscFree(socket->name)); 629 PetscCall(PetscStrallocpy(name, &socket->name)); 630 } 631 socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port; 632 633 PetscCall(PetscViewerSetFromOptions(*viewer)); 634 PetscFunctionReturn(0); 635 } 636 637 /* 638 PETSC_VIEWER_GLVIS_ - Creates a `PETSCVIEWERGLVIS` `PetscViewer` shared by all processors in a communicator. 639 640 Collective 641 642 Input Parameter: 643 . comm - the MPI communicator to share the `PETSCVIEWERGLVIS` `PetscViewer` 644 645 Level: intermediate 646 647 Environmental variables: 648 + `PETSC_VIEWER_GLVIS_FILENAME` : output filename (if specified dump to disk, and takes precedence on `PETSC_VIEWER_GLVIS_HOSTNAME`) 649 . `PETSC_VIEWER_GLVIS_HOSTNAME` : machine where the GLVis server is listening (defaults to localhost) 650 - `PETSC_VIEWER_GLVIS_PORT` : port opened by the GLVis server (defaults to 19916) 651 652 Note: 653 Unlike almost all other PETSc routines, `PETSC_VIEWER_GLVIS_()` does not return 654 an error code. It is usually used in the form 655 $ XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm)); 656 657 Fortran Note: 658 Missing Fortran bindings 659 660 .seealso: `PETSCVIEWERGLVIS`, `PetscViewer`, `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()` 661 */ 662 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) 663 { 664 PetscErrorCode ierr; 665 PetscBool flg; 666 PetscViewer viewer; 667 PetscViewerGLVisType type; 668 char fname[PETSC_MAX_PATH_LEN], sport[16]; 669 PetscInt port = 19916; /* default for GLVis */ 670 671 PetscFunctionBegin; 672 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg); 673 if (ierr) { 674 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 675 PetscFunctionReturn(NULL); 676 } 677 if (!flg) { 678 type = PETSC_VIEWER_GLVIS_SOCKET; 679 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg); 680 if (ierr) { 681 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 682 PetscFunctionReturn(NULL); 683 } 684 if (!flg) { 685 ierr = PetscStrcpy(fname, "localhost"); 686 if (ierr) { 687 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 688 PetscFunctionReturn(NULL); 689 } 690 } 691 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg); 692 if (ierr) { 693 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 694 PetscFunctionReturn(NULL); 695 } 696 if (flg) { 697 ierr = PetscOptionsStringToInt(sport, &port); 698 if (ierr) { 699 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 700 PetscFunctionReturn(NULL); 701 } 702 } 703 } else { 704 type = PETSC_VIEWER_GLVIS_DUMP; 705 } 706 ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer); 707 if (ierr) { 708 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 709 PetscFunctionReturn(NULL); 710 } 711 ierr = PetscObjectRegisterDestroy((PetscObject)viewer); 712 if (ierr) { 713 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 714 PetscFunctionReturn(NULL); 715 } 716 PetscFunctionReturn(viewer); 717 } 718 719 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) 720 { 721 PetscViewerGLVis socket; 722 723 PetscFunctionBegin; 724 PetscCall(PetscNew(&socket)); 725 726 /* defaults to socket viewer */ 727 PetscCall(PetscStrallocpy("localhost", &socket->name)); 728 socket->port = 19916; /* GLVis default listening port */ 729 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 730 socket->pause = 0; /* just pause the first time */ 731 732 socket->windowsizes[0] = 600; 733 socket->windowsizes[1] = 600; 734 735 /* defaults to full precision */ 736 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 737 738 viewer->data = (void *)socket; 739 viewer->ops->destroy = PetscViewerDestroy_GLVis; 740 viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; 741 742 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis)); 743 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis)); 744 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis)); 745 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis)); 746 PetscFunctionReturn(0); 747 } 748 749 /* this is a private implementation of a SOCKET with ASCII data format 750 GLVis does not currently handle binary socket streams */ 751 #if defined(PETSC_HAVE_UNISTD_H) 752 #include <unistd.h> 753 #endif 754 755 #ifndef PETSC_HAVE_WINDOWS_H 756 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); 757 758 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) 759 { 760 FILE *stream; 761 762 PetscFunctionBegin; 763 PetscCall(PetscViewerASCIIGetPointer(viewer, &stream)); 764 if (stream) { 765 int retv = fclose(stream); 766 PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream"); 767 } 768 PetscCall(PetscViewerDestroy_ASCII(viewer)); 769 PetscFunctionReturn(0); 770 } 771 #endif 772 773 /* 774 This attempts to return a NULL viewer if it is unable to open a socket connection. 775 776 The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 777 778 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value 779 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 780 */ 781 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) 782 { 783 #if defined(PETSC_HAVE_WINDOWS_H) 784 PetscFunctionBegin; 785 SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows"); 786 #else 787 FILE *stream = NULL; 788 int fd = 0; 789 PetscErrorCode ierr; 790 791 PetscFunctionBegin; 792 PetscValidCharPointer(hostname, 2); 793 PetscValidPointer(viewer, 4); 794 #if defined(PETSC_USE_SOCKET_VIEWER) 795 ierr = PetscOpenSocket(hostname, port, &fd); 796 #else 797 SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer"); 798 #endif 799 /* 800 The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 801 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value 802 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 803 */ 804 if (PetscUnlikely(ierr)) { 805 char err[1024]; 806 807 PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port)); 808 PetscCall(PetscInfo(NULL, "%s", err)); 809 *viewer = NULL; 810 PetscFunctionReturn(0); 811 } else { 812 char msg[1024]; 813 814 PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port)); 815 PetscCall(PetscInfo(NULL, "%s", msg)); 816 } 817 stream = fdopen(fd, "w"); /* Not possible on Windows */ 818 PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port); 819 PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer)); 820 PetscViewerDestroy_ASCII = (*viewer)->ops->destroy; 821 (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket; 822 #endif 823 PetscFunctionReturn(0); 824 } 825 826 #if !defined(PETSC_MISSING_SIGPIPE) 827 828 #include <signal.h> 829 830 #if defined(PETSC_HAVE_WINDOWS_H) 831 #define PETSC_DEVNULL "NUL" 832 #else 833 #define PETSC_DEVNULL "/dev/null" 834 #endif 835 836 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; 837 838 static void (*PetscGLVisSigHandler_save)(int) = NULL; 839 840 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) 841 { 842 PetscGLVisBrokenPipe = PETSC_TRUE; 843 #if !defined(PETSC_MISSING_SIG_IGN) 844 signal(SIGPIPE, SIG_IGN); 845 #endif 846 } 847 848 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 849 { 850 PetscFunctionBegin; 851 PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME); 852 PetscGLVisBrokenPipe = PETSC_FALSE; 853 PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE); 854 PetscFunctionReturn(0); 855 } 856 857 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) 858 { 859 PetscBool flag, brokenpipe; 860 861 PetscFunctionBegin; 862 flag = PetscGLVisBrokenPipe; 863 PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm)); 864 if (brokenpipe) { 865 FILE *sock, *null = fopen(PETSC_DEVNULL, "w"); 866 PetscCall(PetscViewerASCIIGetPointer(*win, &sock)); 867 PetscCall(PetscViewerASCIISetFILE(*win, null)); 868 PetscCall(PetscViewerDestroy(win)); 869 if (sock) (void)fclose(sock); 870 } 871 (void)signal(SIGPIPE, PetscGLVisSigHandler_save); 872 PetscGLVisSigHandler_save = NULL; 873 PetscGLVisBrokenPipe = PETSC_FALSE; 874 PetscFunctionReturn(0); 875 } 876 877 #else 878 879 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 880 { 881 PetscFunctionBegin; 882 PetscFunctionReturn(0); 883 } 884 885 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) 886 { 887 PetscFunctionBegin; 888 PetscFunctionReturn(0); 889 } 890 891 #endif 892