1 #include <petsc/private/dmnetworkimpl.h> /*I "petscdmnetwork.h" I*/ 2 3 static PetscErrorCode DMView_Network_CSV(DM dm, PetscViewer viewer) 4 { 5 DM dmcoords; 6 PetscInt nsubnets, i, subnet, nvertices, nedges, vertex, edge; 7 PetscInt vertexOffsets[2], globalEdgeVertices[2]; 8 PetscScalar vertexCoords[2]; 9 const PetscInt *vertices, *edges, *edgeVertices; 10 Vec allVertexCoords; 11 PetscMPIInt rank; 12 MPI_Comm comm; 13 14 PetscFunctionBegin; 15 // Get the network containing coordinate information 16 PetscCall(DMGetCoordinateDM(dm, &dmcoords)); 17 // Get the coordinate vector for the network 18 PetscCall(DMGetCoordinatesLocal(dm, &allVertexCoords)); 19 // Get the MPI communicator and this process' rank 20 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 21 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 22 // Start synchronized printing 23 PetscCall(PetscViewerASCIIPushSynchronized(viewer)); 24 25 // Write the header 26 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Type,Rank,ID,X,Y,Z,Name,Color\n")); 27 28 // Iterate each subnetwork (Note: We need to get the global number of subnets apparently) 29 PetscCall(DMNetworkGetNumSubNetworks(dm, NULL, &nsubnets)); 30 for (subnet = 0; subnet < nsubnets; subnet++) { 31 // Get the subnetwork's vertices and edges 32 PetscCall(DMNetworkGetSubnetwork(dm, subnet, &nvertices, &nedges, &vertices, &edges)); 33 34 // Write out each vertex 35 for (i = 0; i < nvertices; i++) { 36 vertex = vertices[i]; 37 // Get the offset into the coordinate vector for the vertex 38 PetscCall(DMNetworkGetLocalVecOffset(dmcoords, vertex, ALL_COMPONENTS, vertexOffsets)); 39 vertexOffsets[1] = vertexOffsets[0] + 1; 40 // Remap vertex to the global value 41 PetscCall(DMNetworkGetGlobalVertexIndex(dm, vertex, &vertex)); 42 // Get the vertex position from the coordinate vector 43 PetscCall(VecGetValues(allVertexCoords, 2, vertexOffsets, vertexCoords)); 44 45 // TODO: Determine vertex color/name 46 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Node,%" PetscInt_FMT ",%" PetscInt_FMT ",%lf,%lf,0,%" PetscInt_FMT "\n", (PetscInt)rank, vertex, (double)PetscRealPart(vertexCoords[0]), (double)PetscRealPart(vertexCoords[1]), vertex)); 47 } 48 49 // Write out each edge 50 for (i = 0; i < nedges; i++) { 51 edge = edges[i]; 52 PetscCall(DMNetworkGetConnectedVertices(dm, edge, &edgeVertices)); 53 PetscCall(DMNetworkGetGlobalVertexIndex(dm, edgeVertices[0], &globalEdgeVertices[0])); 54 PetscCall(DMNetworkGetGlobalVertexIndex(dm, edgeVertices[1], &globalEdgeVertices[1])); 55 PetscCall(DMNetworkGetGlobalEdgeIndex(dm, edge, &edge)); 56 57 // TODO: Determine edge color/name 58 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Edge,%" PetscInt_FMT ",%" PetscInt_FMT ",%" PetscInt_FMT ",%" PetscInt_FMT ",0,%" PetscInt_FMT "\n", (PetscInt)rank, edge, globalEdgeVertices[0], globalEdgeVertices[1], edge)); 59 } 60 } 61 // End synchronized printing 62 PetscCall(PetscViewerFlush(viewer)); 63 PetscCall(PetscViewerASCIIPopSynchronized(viewer)); 64 PetscFunctionReturn(PETSC_SUCCESS); 65 } 66 67 #include <petscdraw.h> 68 static PetscErrorCode DMView_Network_Matplotlib(DM dm, PetscViewer viewer) 69 { 70 PetscMPIInt rank, size; 71 MPI_Comm comm; 72 char filename[PETSC_MAX_PATH_LEN + 1], proccall[PETSC_MAX_PATH_LEN + 500], scriptFile[PETSC_MAX_PATH_LEN + 1], streamBuffer[256]; 73 PetscViewer csvViewer; 74 FILE *processFile = NULL; 75 PetscBool isnull; 76 PetscDraw draw; 77 #if defined(PETSC_HAVE_MKSTEMP) 78 PetscBool isSharedTmp; 79 #endif 80 81 PetscFunctionBegin; 82 // Deal with the PetscDraw we are given 83 PetscCall(PetscViewerDrawGetDraw(viewer, 1, &draw)); 84 PetscCall(PetscDrawIsNull(draw, &isnull)); 85 PetscCall(PetscDrawSetVisible(draw, PETSC_FALSE)); 86 87 // Clear the file name buffer so all communicated bytes are well-defined 88 PetscCall(PetscMemzero(filename, sizeof(filename))); 89 90 // Get the MPI communicator and this process' rank 91 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 92 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 93 PetscCallMPI(MPI_Comm_size(comm, &size)); 94 95 #if defined(PETSC_HAVE_MKSTEMP) 96 // Get if the temporary directory is shared 97 // Note: This must be done collectively on every rank, it cannot be done on a single rank 98 PetscCall(PetscSharedTmp(comm, &isSharedTmp)); 99 #endif 100 101 // Generate and broadcast the temporary file name from rank 0 102 if (rank == 0) { 103 #if defined(PETSC_HAVE_TMPNAM_S) 104 // Acquire a temporary file to write to and open an ASCII/CSV viewer 105 PetscCheck(tmpnam_s(filename, sizeof(filename)) == 0, comm, PETSC_ERR_SYS, "Could not acquire temporary file"); 106 #elif defined(PETSC_HAVE_MKSTEMP) && __STDC_VERSION__ > 199901L 107 PetscBool isTmpOverridden; 108 size_t numChars; 109 // Same thing, but for POSIX systems on which tmpnam is deprecated 110 // Note: Configure may detect mkstemp but it will not be defined if compiling for C99, so check additional defines to see if we can use it 111 // Mkstemp requires us to explicitly specify part of the path, but some systems may not like putting files in /tmp/ so have an option for it 112 PetscCall(PetscOptionsGetString(NULL, NULL, "-dmnetwork_view_tmpdir", filename, sizeof(filename), &isTmpOverridden)); 113 // If not specified by option try using a shared tmp on the system 114 if (!isTmpOverridden) { 115 // Validate that if tmp is not overridden it is at least shared 116 PetscCheck(isSharedTmp, comm, PETSC_ERR_SUP_SYS, "Temporary file directory is not shared between ranks, try using -dmnetwork_view_tmpdir to specify a shared directory"); 117 PetscCall(PetscGetTmp(PETSC_COMM_SELF, filename, sizeof(filename))); 118 } 119 // Make sure the filename ends with a '/' 120 PetscCall(PetscStrlen(filename, &numChars)); 121 if (filename[numChars - 1] != '/') { 122 filename[numChars] = '/'; 123 filename[numChars + 1] = 0; 124 } 125 // Perform the actual temporary file creation 126 PetscCall(PetscStrlcat(filename, "XXXXXX", sizeof(filename))); 127 PetscCheck(mkstemp(filename) != -1, comm, PETSC_ERR_SYS, "Could not acquire temporary file"); 128 #else 129 // Same thing, but for older C versions which don't have the safe form 130 PetscCheck(tmpnam(filename) != NULL, comm, PETSC_ERR_SYS, "Could not acquire temporary file"); 131 #endif 132 } 133 134 // Broadcast the filename to all other MPI ranks 135 PetscCallMPI(MPI_Bcast(filename, PETSC_MAX_PATH_LEN, MPI_BYTE, 0, comm)); 136 137 PetscCall(PetscViewerASCIIOpen(PETSC_COMM_WORLD, filename, &csvViewer)); 138 PetscCall(PetscViewerPushFormat(csvViewer, PETSC_VIEWER_ASCII_CSV)); 139 140 // Use the CSV viewer to write out the local network 141 PetscCall(DMView_Network_CSV(dm, csvViewer)); 142 143 // Close the viewer 144 PetscCall(PetscViewerDestroy(&csvViewer)); 145 146 // Get the value of $PETSC_DIR 147 PetscCall(PetscStrreplace(PETSC_COMM_WORLD, "${PETSC_DIR}/share/petsc/bin/dmnetwork_view.py", scriptFile, sizeof(scriptFile))); 148 PetscCall(PetscFixFilename(scriptFile, scriptFile)); 149 // Generate the system call for 'python3 $PETSC_DIR/share/petsc/dmnetwork_view.py <file>' 150 PetscCall(PetscArrayzero(proccall, sizeof(proccall))); 151 PetscCall(PetscSNPrintf(proccall, sizeof(proccall), "%s %s %s %s", PETSC_PYTHON_EXE, scriptFile, (isnull ? "-tx" : ""), filename)); 152 153 #if defined(PETSC_HAVE_POPEN) 154 // Perform the call to run the python script (Note: while this is called on all ranks POpen will only run on rank 0) 155 PetscCall(PetscPOpen(PETSC_COMM_WORLD, NULL, proccall, "r", &processFile)); 156 if (processFile != NULL) { 157 while (fgets(streamBuffer, sizeof(streamBuffer), processFile) != NULL) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%s", streamBuffer)); 158 } 159 PetscCall(PetscPClose(PETSC_COMM_WORLD, processFile)); 160 #else 161 // Same thing, but using the standard library for systems that don't have POpen/PClose (only run on rank 0) 162 if (rank == 0) { 163 PetscCheck(system(proccall) == 0, comm, PETSC_ERR_SYS, "Failed to call viewer script"); 164 // Barrier so that all ranks wait until the call completes 165 PetscCallMPI(MPI_Barrier(PETSC_COMM_WORLD)); 166 } 167 #endif 168 // Clean up the temporary file we used using rank 0 169 if (rank == 0) PetscCheck(remove(filename) == 0, comm, PETSC_ERR_SYS, "Failed to delete temporary file"); 170 PetscFunctionReturn(PETSC_SUCCESS); 171 } 172 173 PetscErrorCode DMView_Network(DM dm, PetscViewer viewer) 174 { 175 PetscBool iascii, isdraw; 176 PetscViewerFormat format; 177 178 PetscFunctionBegin; 179 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 180 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2); 181 PetscCall(PetscViewerGetFormat(viewer, &format)); 182 183 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw)); 184 if (isdraw) { 185 PetscCall(DMView_Network_Matplotlib(dm, viewer)); 186 PetscFunctionReturn(PETSC_SUCCESS); 187 } 188 189 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 190 if (iascii) { 191 const PetscInt *cone, *vtx, *edges; 192 PetscInt vfrom, vto, i, j, nv, ne, nsv, p, nsubnet; 193 DM_Network *network = (DM_Network *)dm->data; 194 PetscMPIInt rank; 195 196 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 197 if (format == PETSC_VIEWER_ASCII_CSV) { 198 PetscCall(DMView_Network_CSV(dm, viewer)); 199 PetscFunctionReturn(PETSC_SUCCESS); 200 } 201 202 nsubnet = network->cloneshared->Nsubnet; /* num of subnetworks */ 203 if (!rank) { 204 PetscCall(PetscPrintf(PETSC_COMM_SELF, " NSubnets: %" PetscInt_FMT "; NEdges: %" PetscInt_FMT "; NVertices: %" PetscInt_FMT "; NSharedVertices: %" PetscInt_FMT ".\n", nsubnet, network->cloneshared->NEdges, network->cloneshared->NVertices, 205 network->cloneshared->Nsvtx)); 206 } 207 208 PetscCall(DMNetworkGetSharedVertices(dm, &nsv, NULL)); 209 PetscCall(PetscViewerASCIIPushSynchronized(viewer)); 210 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " [%d] nEdges: %" PetscInt_FMT "; nVertices: %" PetscInt_FMT "; nSharedVertices: %" PetscInt_FMT "\n", rank, network->cloneshared->nEdges, network->cloneshared->nVertices, nsv)); 211 212 for (i = 0; i < nsubnet; i++) { 213 PetscCall(DMNetworkGetSubnetwork(dm, i, &nv, &ne, &vtx, &edges)); 214 if (ne) { 215 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " Subnet %" PetscInt_FMT ": nEdges %" PetscInt_FMT ", nVertices(include shared vertices) %" PetscInt_FMT "\n", i, ne, nv)); 216 for (j = 0; j < ne; j++) { 217 p = edges[j]; 218 PetscCall(DMNetworkGetConnectedVertices(dm, p, &cone)); 219 PetscCall(DMNetworkGetGlobalVertexIndex(dm, cone[0], &vfrom)); 220 PetscCall(DMNetworkGetGlobalVertexIndex(dm, cone[1], &vto)); 221 PetscCall(DMNetworkGetGlobalEdgeIndex(dm, edges[j], &p)); 222 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " edge %" PetscInt_FMT ": %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", p, vfrom, vto)); 223 } 224 } 225 } 226 227 /* Shared vertices */ 228 PetscCall(DMNetworkGetSharedVertices(dm, NULL, &vtx)); 229 if (nsv) { 230 PetscInt gidx; 231 PetscBool ghost; 232 const PetscInt *sv = NULL; 233 234 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " SharedVertices:\n")); 235 for (i = 0; i < nsv; i++) { 236 PetscCall(DMNetworkIsGhostVertex(dm, vtx[i], &ghost)); 237 if (ghost) continue; 238 239 PetscCall(DMNetworkSharedVertexGetInfo(dm, vtx[i], &gidx, &nv, &sv)); 240 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " svtx %" PetscInt_FMT ": global index %" PetscInt_FMT ", subnet[%" PetscInt_FMT "].%" PetscInt_FMT " ---->\n", i, gidx, sv[0], sv[1])); 241 for (j = 1; j < nv; j++) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " ----> subnet[%" PetscInt_FMT "].%" PetscInt_FMT "\n", sv[2 * j], sv[2 * j + 1])); 242 } 243 } 244 PetscCall(PetscViewerFlush(viewer)); 245 PetscCall(PetscViewerASCIIPopSynchronized(viewer)); 246 } else PetscCheck(iascii, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMNetwork writing", ((PetscObject)viewer)->type_name); 247 PetscFunctionReturn(PETSC_SUCCESS); 248 } 249