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