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