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 35 36 Not Collective 37 38 Input Parameters: 39 + viewer - the PetscViewer 40 - prec - the number of digits required 41 42 Level: beginner 43 44 .seealso: `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 45 @*/ 46 PetscErrorCode PetscViewerGLVisSetPrecision(PetscViewer viewer, PetscInt prec) { 47 PetscFunctionBegin; 48 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 49 PetscTryMethod(viewer, "PetscViewerGLVisSetPrecision_C", (PetscViewer, PetscInt), (viewer, prec)); 50 PetscFunctionReturn(0); 51 } 52 53 static PetscErrorCode PetscViewerGLVisSetPrecision_GLVis(PetscViewer viewer, PetscInt prec) { 54 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 55 56 PetscFunctionBegin; 57 PetscCall(PetscFree(socket->fmt)); 58 if (prec > 0) { 59 PetscCall(PetscMalloc1(16, &socket->fmt)); 60 PetscCall(PetscSNPrintf(socket->fmt, 16, " %%.%" PetscInt_FMT "e", prec)); 61 } else { 62 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 63 } 64 PetscFunctionReturn(0); 65 } 66 67 /*@ 68 PetscViewerGLVisSetSnapId - Set the snapshot id. Only relevant when the viewer is of type PETSC_VIEWER_GLVIS_DUMP 69 70 Logically Collective on PetscViewer 71 72 Input Parameters: 73 + viewer - the PetscViewer 74 - id - the current snapshot id in a time-dependent simulation 75 76 Level: beginner 77 78 .seealso: `PetscViewerGLVisOpen()`, `PetscViewerGLVisSetFields()`, `PetscViewerCreate()`, `PetscViewerSetType()` 79 @*/ 80 PetscErrorCode PetscViewerGLVisSetSnapId(PetscViewer viewer, PetscInt id) { 81 PetscFunctionBegin; 82 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 83 PetscValidLogicalCollectiveInt(viewer, id, 2); 84 PetscTryMethod(viewer, "PetscViewerGLVisSetSnapId_C", (PetscViewer, PetscInt), (viewer, id)); 85 PetscFunctionReturn(0); 86 } 87 88 static PetscErrorCode PetscViewerGLVisSetSnapId_GLVis(PetscViewer viewer, PetscInt id) { 89 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 90 91 PetscFunctionBegin; 92 socket->snapid = id; 93 PetscFunctionReturn(0); 94 } 95 96 /*@C 97 PetscViewerGLVisSetFields - Sets the required information to visualize different fields from a vector. 98 99 Logically Collective on PetscViewer 100 101 Input Parameters: 102 + viewer - the PetscViewer 103 . nf - number of fields to be visualized 104 . fec_type - the type of finite element to be used to visualize the data (see FiniteElementCollection::Name() in MFEM) 105 . dim - array of space dimension for field vectors (used to initialize the scene) 106 . g2lfields - User routine to compute the local field vectors to be visualized; PetscObject is used in place of Vec on the prototype 107 . Vfield - array of work vectors, one for each field 108 . ctx - User context to store the relevant data to apply g2lfields 109 - destroyctx - Destroy function for userctx 110 111 Notes: 112 g2lfields is called on the vector V to be visualized in order to extract the relevant dofs to be put in Vfield[], as 113 .vb 114 g2lfields((PetscObject)V,nfields,(PetscObject*)Vfield[],ctx). 115 .ve 116 For vector spaces, the block size of Vfield[i] represents the vector dimension. It misses the Fortran bindings. 117 The names of the Vfield vectors will be displayed in the window title. 118 119 Level: intermediate 120 121 .seealso: `PetscViewerGLVisOpen()`, `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscObjectSetName()` 122 @*/ 123 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 *)) { 124 PetscFunctionBegin; 125 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 1); 126 PetscValidLogicalCollectiveInt(viewer, nf, 2); 127 PetscCheck(fec_type, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "You need to provide the FiniteElementCollection names for the fields"); 128 PetscValidPointer(fec_type, 3); 129 PetscValidIntPointer(dim, 4); 130 PetscValidPointer(Vfield, 6); 131 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)); 132 PetscFunctionReturn(0); 133 } 134 135 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 *)) { 136 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 137 PetscInt i; 138 139 PetscFunctionBegin; 140 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); 141 if (!socket->nwindow) { 142 socket->nwindow = nfields; 143 144 PetscCall(PetscCalloc5(nfields, &socket->window, nfields, &socket->windowtitle, nfields, &socket->fec_type, nfields, &socket->spacedim, nfields, &socket->Ufield)); 145 for (i = 0; i < nfields; i++) { 146 const char *name; 147 148 PetscCall(PetscObjectGetName(Vfield[i], &name)); 149 PetscCall(PetscStrallocpy(name, &socket->windowtitle[i])); 150 PetscCall(PetscStrallocpy(fec_type[i], &socket->fec_type[i])); 151 PetscCall(PetscObjectReference(Vfield[i])); 152 socket->Ufield[i] = Vfield[i]; 153 socket->spacedim[i] = dim[i]; 154 } 155 } 156 /* number of fields are not allowed to vary */ 157 PetscCheck(nfields == socket->nwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot visualize %" PetscInt_FMT " fields using %" PetscInt_FMT " socket windows", nfields, socket->nwindow); 158 socket->g2lfield = g2l; 159 if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); 160 socket->userctx = ctx; 161 socket->destroyctx = destroyctx; 162 PetscFunctionReturn(0); 163 } 164 165 static PetscErrorCode PetscViewerGLVisInfoDestroy_Private(void *ptr) { 166 PetscViewerGLVisInfo info = (PetscViewerGLVisInfo)ptr; 167 168 PetscFunctionBegin; 169 PetscCall(PetscFree(info->fmt)); 170 PetscCall(PetscFree(info)); 171 PetscFunctionReturn(0); 172 } 173 174 /* we can decide to prevent specific processes from using the viewer */ 175 static PetscErrorCode PetscViewerGLVisAttachInfo_Private(PetscViewer viewer, PetscViewer window) { 176 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 177 PetscContainer container; 178 PetscViewerGLVisInfo info; 179 180 PetscFunctionBegin; 181 PetscCall(PetscObjectQuery((PetscObject)window, "_glvis_info_container", (PetscObject *)&container)); 182 if (!container) { 183 PetscCall(PetscNew(&info)); 184 info->enabled = PETSC_TRUE; 185 info->init = PETSC_FALSE; 186 info->size[0] = socket->windowsizes[0]; 187 info->size[1] = socket->windowsizes[1]; 188 info->pause = socket->pause; 189 PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)window), &container)); 190 PetscCall(PetscContainerSetPointer(container, (void *)info)); 191 PetscCall(PetscContainerSetUserDestroy(container, PetscViewerGLVisInfoDestroy_Private)); 192 PetscCall(PetscObjectCompose((PetscObject)window, "_glvis_info_container", (PetscObject)container)); 193 PetscCall(PetscContainerDestroy(&container)); 194 } else { 195 PetscCall(PetscContainerGetPointer(container, (void **)&info)); 196 } 197 PetscCall(PetscFree(info->fmt)); 198 PetscCall(PetscStrallocpy(socket->fmt, &info->fmt)); 199 PetscFunctionReturn(0); 200 } 201 202 static PetscErrorCode PetscViewerGLVisGetNewWindow_Private(PetscViewer viewer, PetscViewer *view) { 203 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 204 PetscViewer window = NULL; 205 PetscBool ldis, dis; 206 207 PetscFunctionBegin; 208 PetscCall(PetscViewerASCIISocketOpen(PETSC_COMM_SELF, socket->name, socket->port, &window)); 209 /* if we could not estabilish a connection, we disable the socket viewer on all MPI ranks */ 210 ldis = !viewer ? PETSC_TRUE : PETSC_FALSE; 211 PetscCall(MPIU_Allreduce(&ldis, &dis, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)viewer))); 212 if (dis) { 213 socket->status = PETSCVIEWERGLVIS_DISABLED; 214 PetscCall(PetscViewerDestroy(&window)); 215 } 216 *view = window; 217 PetscFunctionReturn(0); 218 } 219 220 PetscErrorCode PetscViewerGLVisPause_Private(PetscViewer viewer) { 221 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 222 223 PetscFunctionBegin; 224 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET && socket->pause > 0) PetscCall(PetscSleep(socket->pause)); 225 PetscFunctionReturn(0); 226 } 227 228 /* DM specific support */ 229 PetscErrorCode PetscViewerGLVisSetDM_Private(PetscViewer viewer, PetscObject dm) { 230 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 231 232 PetscFunctionBegin; 233 PetscCheck(!socket->dm || socket->dm == dm, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Cannot change DM associated with the GLVis viewer"); 234 if (!socket->dm) { 235 PetscErrorCode (*setupwithdm)(PetscObject, PetscViewer) = NULL; 236 237 PetscCall(PetscObjectQueryFunction(dm, "DMSetUpGLVisViewer_C", &setupwithdm)); 238 if (setupwithdm) { 239 PetscCall((*setupwithdm)(dm, viewer)); 240 } else SETERRQ(PetscObjectComm(dm), PETSC_ERR_SUP, "No support for DM type %s", dm->type_name); 241 PetscCall(PetscObjectReference(dm)); 242 socket->dm = dm; 243 } 244 PetscFunctionReturn(0); 245 } 246 247 PetscErrorCode PetscViewerGLVisGetDMWindow_Private(PetscViewer viewer, PetscViewer *view) { 248 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 249 250 PetscFunctionBegin; 251 PetscValidPointer(view, 2); 252 if (!socket->meshwindow) { 253 if (socket->type == PETSC_VIEWER_GLVIS_SOCKET) { 254 PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->meshwindow)); 255 } else { 256 size_t len; 257 PetscBool isstdout; 258 259 PetscCall(PetscStrlen(socket->name, &len)); 260 PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); 261 if (!socket->name || !len || isstdout) { 262 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->meshwindow)); 263 } else { 264 PetscMPIInt rank; 265 char filename[PETSC_MAX_PATH_LEN]; 266 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); 267 PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-mesh.%06d", socket->name, rank)); 268 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->meshwindow)); 269 } 270 } 271 if (socket->meshwindow) PetscCall(PetscViewerPushFormat(socket->meshwindow, PETSC_VIEWER_ASCII_GLVIS)); 272 } 273 if (socket->meshwindow) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, socket->meshwindow)); 274 *view = socket->meshwindow; 275 PetscFunctionReturn(0); 276 } 277 278 PetscErrorCode PetscViewerGLVisRestoreDMWindow_Private(PetscViewer viewer, PetscViewer *view) { 279 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 280 281 PetscFunctionBegin; 282 PetscValidPointer(view, 2); 283 PetscCheck(!*view || *view == socket->meshwindow, PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetDMWindow()"); 284 if (*view) { 285 PetscCall(PetscViewerFlush(*view)); 286 PetscCall(PetscBarrier((PetscObject)viewer)); 287 } 288 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ 289 PetscCall(PetscViewerDestroy(&socket->meshwindow)); 290 } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ 291 socket->meshwindow = NULL; 292 } 293 *view = NULL; 294 PetscFunctionReturn(0); 295 } 296 297 PetscErrorCode PetscViewerGLVisGetType_Private(PetscViewer viewer, PetscViewerGLVisType *type) { 298 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 299 300 PetscFunctionBegin; 301 PetscValidPointer(type, 2); 302 *type = socket->type; 303 PetscFunctionReturn(0); 304 } 305 306 /* 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 */ 307 PetscErrorCode PetscViewerGLVisGetStatus_Private(PetscViewer viewer, PetscViewerGLVisStatus *sockstatus) { 308 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 309 310 PetscFunctionBegin; 311 PetscValidPointer(sockstatus, 2); 312 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { 313 socket->status = PETSCVIEWERGLVIS_DISCONNECTED; 314 } else if (socket->status == PETSCVIEWERGLVIS_DISCONNECTED && socket->nwindow) { 315 PetscInt i; 316 PetscBool lconn, conn; 317 318 for (i = 0, lconn = PETSC_TRUE; i < socket->nwindow; i++) 319 if (!socket->window[i]) lconn = PETSC_FALSE; 320 321 PetscCall(MPIU_Allreduce(&lconn, &conn, 1, MPIU_BOOL, MPI_LAND, PetscObjectComm((PetscObject)viewer))); 322 if (conn) socket->status = PETSCVIEWERGLVIS_CONNECTED; 323 } 324 *sockstatus = socket->status; 325 PetscFunctionReturn(0); 326 } 327 328 PetscErrorCode PetscViewerGLVisGetDM_Private(PetscViewer viewer, PetscObject *dm) { 329 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 330 331 PetscFunctionBegin; 332 *dm = socket->dm; 333 PetscFunctionReturn(0); 334 } 335 336 PetscErrorCode PetscViewerGLVisGetFields_Private(PetscViewer viewer, PetscInt *nfield, const char **fec[], PetscInt *spacedim[], PetscErrorCode (**g2lfield)(PetscObject, PetscInt, PetscObject[], void *), PetscObject *Ufield[], void **userctx) { 337 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 338 339 PetscFunctionBegin; 340 if (nfield) *nfield = socket->nwindow; 341 if (fec) *fec = (const char **)socket->fec_type; 342 if (spacedim) *spacedim = socket->spacedim; 343 if (g2lfield) *g2lfield = socket->g2lfield; 344 if (Ufield) *Ufield = socket->Ufield; 345 if (userctx) *userctx = socket->userctx; 346 PetscFunctionReturn(0); 347 } 348 349 /* accessor routines for the viewer windows: 350 PETSC_VIEWER_GLVIS_DUMP : it returns a new viewer every time 351 PETSC_VIEWER_GLVIS_SOCKET : it returns the socket, and creates it if not yet done. 352 */ 353 PetscErrorCode PetscViewerGLVisGetWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) { 354 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 355 PetscViewerGLVisStatus status; 356 357 PetscFunctionBegin; 358 PetscValidLogicalCollectiveInt(viewer, wid, 2); 359 PetscValidPointer(view, 3); 360 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); 361 status = socket->status; 362 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); 363 switch (status) { 364 case PETSCVIEWERGLVIS_DISCONNECTED: 365 PetscCheck(!socket->window[wid], PETSC_COMM_SELF, PETSC_ERR_USER, "This should not happen"); 366 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { 367 size_t len; 368 PetscBool isstdout; 369 370 PetscCall(PetscStrlen(socket->name, &len)); 371 PetscCall(PetscStrcmp(socket->name, "stdout", &isstdout)); 372 if (!socket->name || !len || isstdout) { 373 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &socket->window[wid])); 374 } else { 375 PetscMPIInt rank; 376 char filename[PETSC_MAX_PATH_LEN]; 377 378 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank)); 379 PetscCall(PetscSNPrintf(filename, PETSC_MAX_PATH_LEN, "%s-%s-%" PetscInt_FMT ".%06d", socket->name, socket->windowtitle[wid], socket->snapid, rank)); 380 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, filename, &socket->window[wid])); 381 } 382 } else { 383 PetscCall(PetscViewerGLVisGetNewWindow_Private(viewer, &socket->window[wid])); 384 } 385 if (socket->window[wid]) PetscCall(PetscViewerPushFormat(socket->window[wid], PETSC_VIEWER_ASCII_GLVIS)); 386 *view = socket->window[wid]; 387 break; 388 case PETSCVIEWERGLVIS_CONNECTED: *view = socket->window[wid]; break; 389 case PETSCVIEWERGLVIS_DISABLED: *view = NULL; break; 390 default: SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unhandled socket status %d", (int)status); 391 } 392 if (*view) PetscCall(PetscViewerGLVisAttachInfo_Private(viewer, *view)); 393 PetscFunctionReturn(0); 394 } 395 396 /* Restore the window viewer 397 PETSC_VIEWER_GLVIS_DUMP : destroys the temporary created ASCII viewer used for dumping 398 PETSC_VIEWER_GLVIS_SOCKET: - if the returned window viewer is not NULL, just zeros the pointer. 399 - it the returned window viewer is NULL, assumes something went wrong 400 with the socket (i.e. SIGPIPE when a user closes the popup window) 401 and that the caller already handled it (see VecView_GLVis). 402 */ 403 PetscErrorCode PetscViewerGLVisRestoreWindow_Private(PetscViewer viewer, PetscInt wid, PetscViewer *view) { 404 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 405 406 PetscFunctionBegin; 407 PetscValidHeaderSpecificType(viewer, PETSC_VIEWER_CLASSID, 1, PETSCVIEWERGLVIS); 408 PetscValidLogicalCollectiveInt(viewer, wid, 2); 409 PetscValidPointer(view, 3); 410 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); 411 PetscCheck(!*view || *view == socket->window[wid], PetscObjectComm((PetscObject)viewer), PETSC_ERR_USER, "Viewer was not obtained from PetscViewerGLVisGetWindow()"); 412 if (*view) { 413 PetscCall(PetscViewerFlush(*view)); 414 PetscCall(PetscBarrier((PetscObject)viewer)); 415 } 416 if (socket->type == PETSC_VIEWER_GLVIS_DUMP) { /* destroy the viewer, as it is associated with a single time step */ 417 PetscCall(PetscViewerDestroy(&socket->window[wid])); 418 } else if (!*view) { /* something went wrong (SIGPIPE) so we just zero the private pointer */ 419 socket->window[wid] = NULL; 420 } 421 *view = NULL; 422 PetscFunctionReturn(0); 423 } 424 425 /* default window appearance in the PETSC_VIEWER_GLVIS_SOCKET case */ 426 PetscErrorCode PetscViewerGLVisInitWindow_Private(PetscViewer viewer, PetscBool mesh, PetscInt dim, const char *name) { 427 PetscViewerGLVisInfo info; 428 PetscContainer container; 429 430 PetscFunctionBegin; 431 PetscCall(PetscObjectQuery((PetscObject)viewer, "_glvis_info_container", (PetscObject *)&container)); 432 PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_USER, "Viewer was not obtained from PetscGLVisViewerGetNewWindow_Private"); 433 PetscCall(PetscContainerGetPointer(container, (void **)&info)); 434 if (info->init) PetscFunctionReturn(0); 435 436 /* Configure window */ 437 if (info->size[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, "window_size %" PetscInt_FMT " %" PetscInt_FMT "\n", info->size[0], info->size[1])); 438 if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "window_title '%s'\n", name)); 439 440 /* Configure default view */ 441 if (mesh) { 442 switch (dim) { 443 case 1: 444 PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ 445 break; 446 case 2: 447 PetscCall(PetscViewerASCIIPrintf(viewer, "keys m\n")); /* show mesh */ 448 break; 449 case 3: /* TODO: decide default view in 3D */ break; 450 } 451 } else { 452 PetscCall(PetscViewerASCIIPrintf(viewer, "keys cm\n")); /* show colorbar and mesh */ 453 switch (dim) { 454 case 1: 455 PetscCall(PetscViewerASCIIPrintf(viewer, "keys RRjl\n")); /* set to 1D (side view), turn off perspective and light */ 456 break; 457 case 2: 458 PetscCall(PetscViewerASCIIPrintf(viewer, "keys Rjl\n")); /* set to 2D (top view), turn off perspective and light */ 459 break; 460 case 3: break; 461 } 462 PetscCall(PetscViewerASCIIPrintf(viewer, "autoscale value\n")); /* update value-range; keep mesh-extents fixed */ 463 } 464 465 { /* Additional keys and commands */ 466 char keys[256] = "", cmds[2 * PETSC_MAX_PATH_LEN] = ""; 467 PetscOptions opt = ((PetscObject)viewer)->options; 468 const char *pre = ((PetscObject)viewer)->prefix; 469 470 PetscCall(PetscOptionsGetString(opt, pre, "-glvis_keys", keys, sizeof(keys), NULL)); 471 PetscCall(PetscOptionsGetString(opt, pre, "-glvis_exec", cmds, sizeof(cmds), NULL)); 472 if (keys[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "keys %s\n", keys)); 473 if (cmds[0]) PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", cmds)); 474 } 475 476 /* Pause visualization */ 477 if (!mesh && info->pause == -1) PetscCall(PetscViewerASCIIPrintf(viewer, "autopause 1\n")); 478 if (!mesh && info->pause == 0) PetscCall(PetscViewerASCIIPrintf(viewer, "pause\n")); 479 480 info->init = PETSC_TRUE; 481 PetscFunctionReturn(0); 482 } 483 484 static PetscErrorCode PetscViewerDestroy_GLVis(PetscViewer viewer) { 485 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 486 PetscInt i; 487 488 PetscFunctionBegin; 489 for (i = 0; i < socket->nwindow; i++) { 490 PetscCall(PetscViewerDestroy(&socket->window[i])); 491 PetscCall(PetscFree(socket->windowtitle[i])); 492 PetscCall(PetscFree(socket->fec_type[i])); 493 PetscCall(PetscObjectDestroy(&socket->Ufield[i])); 494 } 495 PetscCall(PetscFree(socket->name)); 496 PetscCall(PetscFree5(socket->window, socket->windowtitle, socket->fec_type, socket->spacedim, socket->Ufield)); 497 PetscCall(PetscFree(socket->fmt)); 498 PetscCall(PetscViewerDestroy(&socket->meshwindow)); 499 PetscCall(PetscObjectDestroy(&socket->dm)); 500 if (socket->destroyctx && socket->userctx) PetscCall((*socket->destroyctx)(socket->userctx)); 501 502 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", NULL)); 503 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", NULL)); 504 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", NULL)); 505 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", NULL)); 506 PetscCall(PetscFree(socket)); 507 viewer->data = NULL; 508 PetscFunctionReturn(0); 509 } 510 511 static PetscErrorCode PetscViewerSetFromOptions_GLVis(PetscViewer v, PetscOptionItems *PetscOptionsObject) { 512 PetscViewerGLVis socket = (PetscViewerGLVis)v->data; 513 PetscInt nsizes = 2, prec = PETSC_DECIDE; 514 PetscBool set; 515 516 PetscFunctionBegin; 517 PetscOptionsHeadBegin(PetscOptionsObject, "GLVis PetscViewer Options"); 518 PetscCall(PetscOptionsInt("-glvis_precision", "Number of digits for floating point values", "PetscViewerGLVisSetPrecision", prec, &prec, &set)); 519 if (set) PetscCall(PetscViewerGLVisSetPrecision(v, prec)); 520 PetscCall(PetscOptionsIntArray("-glvis_size", "Window sizes", NULL, socket->windowsizes, &nsizes, &set)); 521 if (set && (nsizes == 1 || socket->windowsizes[1] < 0)) socket->windowsizes[1] = socket->windowsizes[0]; 522 PetscCall(PetscOptionsReal("-glvis_pause", "-1 to pause after each visualization, otherwise sleeps for given seconds", NULL, socket->pause, &socket->pause, NULL)); 523 PetscCall(PetscOptionsName("-glvis_keys", "Additional keys to configure visualization", NULL, NULL)); 524 PetscCall(PetscOptionsName("-glvis_exec", "Additional commands to configure visualization", NULL, NULL)); 525 PetscOptionsHeadEnd(); 526 PetscFunctionReturn(0); 527 } 528 529 static PetscErrorCode PetscViewerFileSetName_GLVis(PetscViewer viewer, const char name[]) { 530 char *sport; 531 PetscViewerGLVis socket = (PetscViewerGLVis)viewer->data; 532 533 PetscFunctionBegin; 534 socket->type = PETSC_VIEWER_GLVIS_DUMP; 535 /* we accept localhost^port */ 536 PetscCall(PetscFree(socket->name)); 537 PetscCall(PetscStrallocpy(name, &socket->name)); 538 PetscCall(PetscStrchr(socket->name, '^', &sport)); 539 if (sport) { 540 PetscInt port = 19916; 541 size_t len; 542 PetscErrorCode ierr; 543 544 *sport++ = 0; 545 PetscCall(PetscStrlen(sport, &len)); 546 ierr = PetscOptionsStringToInt(sport, &port); 547 if (PetscUnlikely(ierr)) { 548 socket->port = 19916; 549 } else { 550 socket->port = (port != PETSC_DECIDE && port != PETSC_DEFAULT) ? port : 19916; 551 } 552 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 553 } 554 PetscFunctionReturn(0); 555 } 556 557 /*@C 558 PetscViewerGLVisOpen - Opens a GLVis type viewer 559 560 Collective 561 562 Input Parameters: 563 + comm - the MPI communicator 564 . type - the viewer type: PETSC_VIEWER_GLVIS_SOCKET for real-time visualization or PETSC_VIEWER_GLVIS_DUMP for dumping to disk 565 . name - either the hostname where the GLVis server is running or the base filename for dumping the data for subsequent visualizations 566 - port - socket port where the GLVis server is listening. Not referenced when type is PETSC_VIEWER_GLVIS_DUMP 567 568 Output Parameters: 569 - viewer - the PetscViewer object 570 571 Options Database Keys: 572 + -glvis_precision <precision> - Sets number of digits for floating point values 573 . -glvis_size <width,height> - Sets the window size (in pixels) 574 . -glvis_pause <pause> - Sets time (in seconds) that the program pauses after each visualization 575 (0 is default, -1 implies every visualization) 576 . -glvis_keys - Additional keys to configure visualization 577 - -glvis_exec - Additional commands to configure visualization 578 579 Notes: 580 misses Fortran binding 581 582 Level: beginner 583 584 .seealso: `PetscViewerCreate()`, `PetscViewerSetType()`, `PetscViewerGLVisType` 585 @*/ 586 PetscErrorCode PetscViewerGLVisOpen(MPI_Comm comm, PetscViewerGLVisType type, const char name[], PetscInt port, PetscViewer *viewer) { 587 PetscViewerGLVis socket; 588 589 PetscFunctionBegin; 590 PetscCall(PetscViewerCreate(comm, viewer)); 591 PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERGLVIS)); 592 593 socket = (PetscViewerGLVis)((*viewer)->data); 594 socket->type = type; 595 if (type == PETSC_VIEWER_GLVIS_DUMP || name) { 596 PetscCall(PetscFree(socket->name)); 597 PetscCall(PetscStrallocpy(name, &socket->name)); 598 } 599 socket->port = (!port || port == PETSC_DETERMINE || port == PETSC_DECIDE) ? 19916 : port; 600 601 PetscCall(PetscViewerSetFromOptions(*viewer)); 602 PetscFunctionReturn(0); 603 } 604 605 /* 606 PETSC_VIEWER_GLVIS_ - Creates an GLVIS PetscViewer shared by all processors in a communicator. 607 608 Collective 609 610 Input Parameter: 611 . comm - the MPI communicator to share the GLVIS PetscViewer 612 613 Level: intermediate 614 615 Notes: 616 misses Fortran bindings 617 618 Environmental variables: 619 + PETSC_VIEWER_GLVIS_FILENAME : output filename (if specified dump to disk, and takes precedence on PETSC_VIEWER_GLVIS_HOSTNAME) 620 . PETSC_VIEWER_GLVIS_HOSTNAME : machine where the GLVis server is listening (defaults to localhost) 621 - PETSC_VIEWER_GLVIS_PORT : port opened by the GLVis server (defaults to 19916) 622 623 Notes: 624 Unlike almost all other PETSc routines, PETSC_VIEWER_GLVIS_ does not return 625 an error code. The GLVIS PetscViewer is usually used in the form 626 $ XXXView(XXX object, PETSC_VIEWER_GLVIS_(comm)); 627 628 .seealso: `PetscViewerGLVISOpen()`, `PetscViewerGLVisType`, `PetscViewerCreate()`, `PetscViewerDestroy()` 629 */ 630 PetscViewer PETSC_VIEWER_GLVIS_(MPI_Comm comm) { 631 PetscErrorCode ierr; 632 PetscBool flg; 633 PetscViewer viewer; 634 PetscViewerGLVisType type; 635 char fname[PETSC_MAX_PATH_LEN], sport[16]; 636 PetscInt port = 19916; /* default for GLVis */ 637 638 PetscFunctionBegin; 639 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg); 640 if (ierr) { 641 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 642 PetscFunctionReturn(NULL); 643 } 644 if (!flg) { 645 type = PETSC_VIEWER_GLVIS_SOCKET; 646 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_HOSTNAME", fname, PETSC_MAX_PATH_LEN, &flg); 647 if (ierr) { 648 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 649 PetscFunctionReturn(NULL); 650 } 651 if (!flg) { 652 ierr = PetscStrcpy(fname, "localhost"); 653 if (ierr) { 654 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 655 PetscFunctionReturn(NULL); 656 } 657 } 658 ierr = PetscOptionsGetenv(comm, "PETSC_VIEWER_GLVIS_PORT", sport, 16, &flg); 659 if (ierr) { 660 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 661 PetscFunctionReturn(NULL); 662 } 663 if (flg) { 664 ierr = PetscOptionsStringToInt(sport, &port); 665 if (ierr) { 666 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 667 PetscFunctionReturn(NULL); 668 } 669 } 670 } else { 671 type = PETSC_VIEWER_GLVIS_DUMP; 672 } 673 ierr = PetscViewerGLVisOpen(comm, type, fname, port, &viewer); 674 if (ierr) { 675 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 676 PetscFunctionReturn(NULL); 677 } 678 ierr = PetscObjectRegisterDestroy((PetscObject)viewer); 679 if (ierr) { 680 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_GLVIS_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 681 PetscFunctionReturn(NULL); 682 } 683 PetscFunctionReturn(viewer); 684 } 685 686 PETSC_EXTERN PetscErrorCode PetscViewerCreate_GLVis(PetscViewer viewer) { 687 PetscViewerGLVis socket; 688 689 PetscFunctionBegin; 690 PetscCall(PetscNewLog(viewer, &socket)); 691 692 /* defaults to socket viewer */ 693 PetscCall(PetscStrallocpy("localhost", &socket->name)); 694 socket->port = 19916; /* GLVis default listening port */ 695 socket->type = PETSC_VIEWER_GLVIS_SOCKET; 696 socket->pause = 0; /* just pause the first time */ 697 698 socket->windowsizes[0] = 600; 699 socket->windowsizes[1] = 600; 700 701 /* defaults to full precision */ 702 PetscCall(PetscStrallocpy(" %g", &socket->fmt)); 703 704 viewer->data = (void *)socket; 705 viewer->ops->destroy = PetscViewerDestroy_GLVis; 706 viewer->ops->setfromoptions = PetscViewerSetFromOptions_GLVis; 707 708 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetPrecision_C", PetscViewerGLVisSetPrecision_GLVis)); 709 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetSnapId_C", PetscViewerGLVisSetSnapId_GLVis)); 710 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerGLVisSetFields_C", PetscViewerGLVisSetFields_GLVis)); 711 PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_GLVis)); 712 PetscFunctionReturn(0); 713 } 714 715 /* this is a private implementation of a SOCKET with ASCII data format 716 GLVis does not currently handle binary socket streams */ 717 #if defined(PETSC_HAVE_UNISTD_H) 718 #include <unistd.h> 719 #endif 720 721 #if !defined(PETSC_HAVE_WINDOWS_H) 722 static PetscErrorCode (*PetscViewerDestroy_ASCII)(PetscViewer); 723 724 static PetscErrorCode PetscViewerDestroy_ASCII_Socket(PetscViewer viewer) { 725 FILE *stream; 726 727 PetscFunctionBegin; 728 PetscCall(PetscViewerASCIIGetPointer(viewer, &stream)); 729 if (stream) { 730 int retv = fclose(stream); 731 PetscCheck(!retv, PETSC_COMM_SELF, PETSC_ERR_SYS, "fclose() failed on stream"); 732 } 733 PetscCall(PetscViewerDestroy_ASCII(viewer)); 734 PetscFunctionReturn(0); 735 } 736 #endif 737 738 /* 739 This attempts to return a NULL viewer if it is unable to open a socket connection. 740 741 The code below involving PetscUnlikely(ierr) is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 742 743 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain failure conditions but instead either return a special value 744 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 745 */ 746 static PetscErrorCode PetscViewerASCIISocketOpen(MPI_Comm comm, const char *hostname, PetscInt port, PetscViewer *viewer) { 747 #if defined(PETSC_HAVE_WINDOWS_H) 748 PetscFunctionBegin; 749 SETERRQ(comm, PETSC_ERR_SUP, "Not implemented for Windows"); 750 #else 751 FILE *stream = NULL; 752 int fd = 0; 753 PetscErrorCode ierr; 754 755 PetscFunctionBegin; 756 PetscValidCharPointer(hostname, 2); 757 PetscValidPointer(viewer, 4); 758 #if defined(PETSC_USE_SOCKET_VIEWER) 759 ierr = PetscOpenSocket(hostname, port, &fd); 760 #else 761 SETERRQ(comm, PETSC_ERR_SUP, "Missing Socket viewer"); 762 #endif 763 /* 764 The following code is illegal in PETSc, one can NEVER attempt to recover once an error is initiated in PETSc. 765 The correct approach is to refactor PetscOpenSocket() to not initiate an error under certain conditions but instead either return a special value 766 of fd to indicate it was impossible to open the socket, or add another return argument to it indicating the socket was not opened. 767 */ 768 if (PetscUnlikely(ierr)) { 769 char err[1024]; 770 771 PetscCall(PetscSNPrintf(err, 1024, "Cannot connect to socket on %s:%" PetscInt_FMT ". Socket visualization is disabled\n", hostname, port)); 772 PetscCall(PetscInfo(NULL, "%s", err)); 773 *viewer = NULL; 774 PetscFunctionReturn(0); 775 } else { 776 char msg[1024]; 777 778 PetscCall(PetscSNPrintf(msg, 1024, "Successfully connect to socket on %s:%" PetscInt_FMT ". Socket visualization is enabled\n", hostname, port)); 779 PetscCall(PetscInfo(NULL, "%s", msg)); 780 } 781 stream = fdopen(fd, "w"); /* Not possible on Windows */ 782 PetscCheck(stream, PETSC_COMM_SELF, PETSC_ERR_SYS, "Cannot open stream from socket %s:%" PetscInt_FMT, hostname, port); 783 PetscCall(PetscViewerASCIIOpenWithFILE(PETSC_COMM_SELF, stream, viewer)); 784 PetscViewerDestroy_ASCII = (*viewer)->ops->destroy; 785 (*viewer)->ops->destroy = PetscViewerDestroy_ASCII_Socket; 786 #endif 787 PetscFunctionReturn(0); 788 } 789 790 #if !defined(PETSC_MISSING_SIGPIPE) 791 792 #include <signal.h> 793 794 #if defined(PETSC_HAVE_WINDOWS_H) 795 #define PETSC_DEVNULL "NUL" 796 #else 797 #define PETSC_DEVNULL "/dev/null" 798 #endif 799 800 static volatile PetscBool PetscGLVisBrokenPipe = PETSC_FALSE; 801 802 static void (*PetscGLVisSigHandler_save)(int) = NULL; 803 804 static void PetscGLVisSigHandler_SIGPIPE(PETSC_UNUSED int sig) { 805 PetscGLVisBrokenPipe = PETSC_TRUE; 806 #if !defined(PETSC_MISSING_SIG_IGN) 807 signal(SIGPIPE, SIG_IGN); 808 #endif 809 } 810 811 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { 812 PetscFunctionBegin; 813 PetscCheck(!PetscGLVisSigHandler_save, comm, PETSC_ERR_PLIB, "Nested call to %s()", PETSC_FUNCTION_NAME); 814 PetscGLVisBrokenPipe = PETSC_FALSE; 815 PetscGLVisSigHandler_save = signal(SIGPIPE, PetscGLVisSigHandler_SIGPIPE); 816 PetscFunctionReturn(0); 817 } 818 819 PetscErrorCode PetscGLVisCollectiveEnd(MPI_Comm comm, PetscViewer *win) { 820 PetscBool flag, brokenpipe; 821 822 PetscFunctionBegin; 823 flag = PetscGLVisBrokenPipe; 824 PetscCall(MPIU_Allreduce(&flag, &brokenpipe, 1, MPIU_BOOL, MPI_LOR, comm)); 825 if (brokenpipe) { 826 FILE *sock, *null = fopen(PETSC_DEVNULL, "w"); 827 PetscCall(PetscViewerASCIIGetPointer(*win, &sock)); 828 PetscCall(PetscViewerASCIISetFILE(*win, null)); 829 PetscCall(PetscViewerDestroy(win)); 830 if (sock) (void)fclose(sock); 831 } 832 (void)signal(SIGPIPE, PetscGLVisSigHandler_save); 833 PetscGLVisSigHandler_save = NULL; 834 PetscGLVisBrokenPipe = PETSC_FALSE; 835 PetscFunctionReturn(0); 836 } 837 838 #else 839 840 PetscErrorCode PetscGLVisCollectiveBegin(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { 841 PetscFunctionBegin; 842 PetscFunctionReturn(0); 843 } 844 845 PetscErrorCode PetscGLVisCollectiveEnd(PETSC_UNUSED MPI_Comm comm, PETSC_UNUSED PetscViewer *win) { 846 PetscFunctionBegin; 847 PetscFunctionReturn(0); 848 } 849 850 #endif 851