13e72e933SJed Brown #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/ 23e72e933SJed Brown #include <petscsf.h> 33e72e933SJed Brown #include <petsc/private/hashset.h> 43e72e933SJed Brown 53e72e933SJed Brown typedef uint64_t ZCode; 63e72e933SJed Brown 73e72e933SJed Brown PETSC_HASH_SET(ZSet, ZCode, PetscHash_UInt64, PetscHashEqual) 83e72e933SJed Brown 93e72e933SJed Brown typedef struct { 103e72e933SJed Brown PetscInt i, j, k; 113e72e933SJed Brown } Ijk; 123e72e933SJed Brown 133e72e933SJed Brown typedef struct { 143e72e933SJed Brown Ijk eextent; 153e72e933SJed Brown Ijk vextent; 163e72e933SJed Brown PetscMPIInt comm_size; 173e72e933SJed Brown ZCode *zstarts; 183e72e933SJed Brown } ZLayout; 193e72e933SJed Brown 203e72e933SJed Brown static unsigned ZCodeSplit1(ZCode z) 213e72e933SJed Brown { 223e72e933SJed Brown z = ((z & 01001001001001001) | ((z >> 2) & 02002002002002002) | ((z >> 4) & 04004004004004004)); 233e72e933SJed Brown z = (z | (z >> 6) | (z >> 12)) & 0000000777000000777; 243e72e933SJed Brown z = (z | (z >> 18)) & 0777777; 253e72e933SJed Brown return (unsigned)z; 263e72e933SJed Brown } 273e72e933SJed Brown 283e72e933SJed Brown static ZCode ZEncode1(unsigned t) 293e72e933SJed Brown { 303e72e933SJed Brown ZCode z = t; 313e72e933SJed Brown z = (z | (z << 18)) & 0777000000777; 323e72e933SJed Brown z = (z | (z << 6) | (z << 12)) & 07007007007007007; 333e72e933SJed Brown z = (z | (z << 2) | (z << 4)) & 0111111111111111111; 343e72e933SJed Brown return z; 353e72e933SJed Brown } 363e72e933SJed Brown 373e72e933SJed Brown static Ijk ZCodeSplit(ZCode z) 383e72e933SJed Brown { 393e72e933SJed Brown Ijk c; 403e72e933SJed Brown c.i = ZCodeSplit1(z >> 2); 413e72e933SJed Brown c.j = ZCodeSplit1(z >> 1); 423e72e933SJed Brown c.k = ZCodeSplit1(z >> 0); 433e72e933SJed Brown return c; 443e72e933SJed Brown } 453e72e933SJed Brown 463e72e933SJed Brown static ZCode ZEncode(Ijk c) 473e72e933SJed Brown { 483e72e933SJed Brown ZCode z = (ZEncode1(c.i) << 2) | (ZEncode1(c.j) << 1) | ZEncode1(c.k); 493e72e933SJed Brown return z; 503e72e933SJed Brown } 513e72e933SJed Brown 523e72e933SJed Brown static PetscBool IjkActive(Ijk extent, Ijk l) 533e72e933SJed Brown { 543e72e933SJed Brown if (l.i < extent.i && l.j < extent.j && l.k < extent.k) return PETSC_TRUE; 553e72e933SJed Brown return PETSC_FALSE; 563e72e933SJed Brown } 573e72e933SJed Brown 583e72e933SJed Brown // Since element/vertex box extents are typically not equal powers of 2, Z codes that lie within the domain are not contiguous. 59*c77877e3SJed Brown static PetscErrorCode ZLayoutCreate(PetscMPIInt size, const PetscInt eextent[3], const PetscInt vextent[3], ZLayout *zlayout) 603e72e933SJed Brown { 613e72e933SJed Brown ZLayout layout; 62*c77877e3SJed Brown 63*c77877e3SJed Brown PetscFunctionBegin; 643e72e933SJed Brown layout.eextent.i = eextent[0]; 653e72e933SJed Brown layout.eextent.j = eextent[1]; 663e72e933SJed Brown layout.eextent.k = eextent[2]; 673e72e933SJed Brown layout.vextent.i = vextent[0]; 683e72e933SJed Brown layout.vextent.j = vextent[1]; 693e72e933SJed Brown layout.vextent.k = vextent[2]; 703e72e933SJed Brown layout.comm_size = size; 71*c77877e3SJed Brown PetscCall(PetscMalloc1(size + 1, &layout.zstarts)); 723e72e933SJed Brown 733e72e933SJed Brown PetscInt total_elems = eextent[0] * eextent[1] * eextent[2]; 743e72e933SJed Brown ZCode z = 0; 753e72e933SJed Brown layout.zstarts[0] = 0; 763e72e933SJed Brown for (PetscMPIInt r = 0; r < size; r++) { 773e72e933SJed Brown PetscInt elems_needed = (total_elems / size) + (total_elems % size > r), count; 783e72e933SJed Brown for (count = 0; count < elems_needed; z++) { 793e72e933SJed Brown Ijk loc = ZCodeSplit(z); 803e72e933SJed Brown if (IjkActive(layout.eextent, loc)) count++; 813e72e933SJed Brown } 823e72e933SJed Brown // Pick up any extra vertices in the Z ordering before the next rank's first owned element. 83*c77877e3SJed Brown // 84*c77877e3SJed Brown // TODO: This leads to poorly balanced vertices when eextent is a power of 2, since all the fringe vertices end up 85*c77877e3SJed Brown // on the last rank. A possible solution is to balance the Z-order vertices independently from the cells, which will 86*c77877e3SJed Brown // result in a lot of element closures being remote. We could finish marking boundary conditions, then do a round of 87*c77877e3SJed Brown // vertex ownership smoothing (which would reorder and redistribute vertices without touching element distribution). 88*c77877e3SJed Brown // Another would be to have an analytic ownership criteria for vertices in the fringe veextent - eextent. This would 89*c77877e3SJed Brown // complicate the job of identifying an owner and its offset. 903e72e933SJed Brown for (; z <= ZEncode(layout.vextent); z++) { 913e72e933SJed Brown Ijk loc = ZCodeSplit(z); 923e72e933SJed Brown if (IjkActive(layout.eextent, loc)) break; 933e72e933SJed Brown } 943e72e933SJed Brown layout.zstarts[r + 1] = z; 953e72e933SJed Brown } 96*c77877e3SJed Brown *zlayout = layout; 97*c77877e3SJed Brown PetscFunctionReturn(0); 983e72e933SJed Brown } 993e72e933SJed Brown 1003e72e933SJed Brown PetscInt ZCodeFind(ZCode key, PetscInt n, const ZCode X[]) 1013e72e933SJed Brown { 1023e72e933SJed Brown PetscInt lo = 0, hi = n; 1033e72e933SJed Brown 1043e72e933SJed Brown if (n == 0) return -1; 1053e72e933SJed Brown while (hi - lo > 1) { 1063e72e933SJed Brown PetscInt mid = lo + (hi - lo) / 2; 1073e72e933SJed Brown if (key < X[mid]) hi = mid; 1083e72e933SJed Brown else lo = mid; 1093e72e933SJed Brown } 1103e72e933SJed Brown return key == X[lo] ? lo : -(lo + (key > X[lo]) + 1); 1113e72e933SJed Brown } 1123e72e933SJed Brown 1133e72e933SJed Brown PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate) 1143e72e933SJed Brown { 1153e72e933SJed Brown PetscInt eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1}; 1163e72e933SJed Brown const Ijk closure_1[] = { 1173e72e933SJed Brown {0, 0, 0}, 1183e72e933SJed Brown {1, 0, 0}, 1193e72e933SJed Brown }; 1203e72e933SJed Brown const Ijk closure_2[] = { 1213e72e933SJed Brown {0, 0, 0}, 1223e72e933SJed Brown {1, 0, 0}, 1233e72e933SJed Brown {1, 1, 0}, 1243e72e933SJed Brown {0, 1, 0}, 1253e72e933SJed Brown }; 1263e72e933SJed Brown const Ijk closure_3[] = { 1273e72e933SJed Brown {0, 0, 0}, 1283e72e933SJed Brown {0, 1, 0}, 1293e72e933SJed Brown {1, 1, 0}, 1303e72e933SJed Brown {1, 0, 0}, 1313e72e933SJed Brown {0, 0, 1}, 1323e72e933SJed Brown {1, 0, 1}, 1333e72e933SJed Brown {1, 1, 1}, 1343e72e933SJed Brown {0, 1, 1}, 1353e72e933SJed Brown }; 1363431e603SJed Brown const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3}; 1373431e603SJed Brown // This must be kept consistent with DMPlexCreateCubeMesh_Internal 1383431e603SJed Brown const PetscInt face_marker_1[] = {1, 2}; 1393431e603SJed Brown const PetscInt face_marker_2[] = {4, 2, 1, 3}; 1403431e603SJed Brown const PetscInt face_marker_3[] = {6, 5, 3, 4, 1, 2}; 1413431e603SJed Brown const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3}; 1423e72e933SJed Brown 1433e72e933SJed Brown PetscFunctionBegin; 1443e72e933SJed Brown PetscValidPointer(dm, 1); 1453e72e933SJed Brown PetscValidLogicalCollectiveInt(dm, dim, 2); 1463e72e933SJed Brown PetscCall(DMSetDimension(dm, dim)); 1473e72e933SJed Brown PetscMPIInt rank, size; 1483e72e933SJed Brown PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size)); 1493e72e933SJed Brown PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 1503e72e933SJed Brown for (PetscInt i = 0; i < dim; i++) { 1513e72e933SJed Brown eextent[i] = faces[i]; 1523e72e933SJed Brown vextent[i] = faces[i] + 1; 1533e72e933SJed Brown } 154*c77877e3SJed Brown ZLayout layout; 155*c77877e3SJed Brown PetscCall(ZLayoutCreate(size, eextent, vextent, &layout)); 1563e72e933SJed Brown PetscZSet vset; // set of all vertices in the closure of the owned elements 1573e72e933SJed Brown PetscCall(PetscZSetCreate(&vset)); 1583e72e933SJed Brown PetscInt local_elems = 0; 1593e72e933SJed Brown for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 1603e72e933SJed Brown Ijk loc = ZCodeSplit(z); 1613e72e933SJed Brown if (IjkActive(layout.vextent, loc)) PetscZSetAdd(vset, z); 1623e72e933SJed Brown if (IjkActive(layout.eextent, loc)) { 1633e72e933SJed Brown local_elems++; 1643e72e933SJed Brown // Add all neighboring vertices to set 1653e72e933SJed Brown for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 1663e72e933SJed Brown Ijk inc = closure_dim[dim][n]; 1673e72e933SJed Brown Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 1683e72e933SJed Brown ZCode v = ZEncode(nloc); 1693e72e933SJed Brown PetscZSetAdd(vset, v); 1703e72e933SJed Brown } 1713e72e933SJed Brown } 1723e72e933SJed Brown } 1733e72e933SJed Brown PetscInt local_verts, off = 0; 1743e72e933SJed Brown ZCode *vert_z; 1753e72e933SJed Brown PetscCall(PetscZSetGetSize(vset, &local_verts)); 1763e72e933SJed Brown PetscCall(PetscMalloc1(local_verts, &vert_z)); 1773e72e933SJed Brown PetscCall(PetscZSetGetElems(vset, &off, vert_z)); 1783e72e933SJed Brown PetscCall(PetscZSetDestroy(&vset)); 1793e72e933SJed Brown // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed 1803e72e933SJed Brown PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z)); 1813e72e933SJed Brown 1823e72e933SJed Brown PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts)); 1833e72e933SJed Brown for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim))); 1843e72e933SJed Brown PetscCall(DMSetUp(dm)); 1853e72e933SJed Brown { 1863e72e933SJed Brown PetscInt e = 0; 1873e72e933SJed Brown for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 1883e72e933SJed Brown Ijk loc = ZCodeSplit(z); 1893e72e933SJed Brown if (!IjkActive(layout.eextent, loc)) continue; 1903e72e933SJed Brown PetscInt cone[8], orient[8] = {0}; 1913e72e933SJed Brown for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 1923e72e933SJed Brown Ijk inc = closure_dim[dim][n]; 1933e72e933SJed Brown Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 1943e72e933SJed Brown ZCode v = ZEncode(nloc); 1953e72e933SJed Brown PetscInt ci = ZCodeFind(v, local_verts, vert_z); 1963e72e933SJed Brown PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set"); 1973e72e933SJed Brown cone[n] = local_elems + ci; 1983e72e933SJed Brown } 1993e72e933SJed Brown PetscCall(DMPlexSetCone(dm, e, cone)); 2003e72e933SJed Brown PetscCall(DMPlexSetConeOrientation(dm, e, orient)); 2013e72e933SJed Brown e++; 2023e72e933SJed Brown } 2033e72e933SJed Brown } 2043e72e933SJed Brown 2053e72e933SJed Brown if (0) { 2063e72e933SJed Brown DMLabel depth; 2073e72e933SJed Brown PetscCall(DMCreateLabel(dm, "depth")); 2083e72e933SJed Brown PetscCall(DMPlexGetDepthLabel(dm, &depth)); 2093e72e933SJed Brown PetscCall(DMLabelSetStratumBounds(depth, 0, local_elems, local_elems + local_verts)); 2103e72e933SJed Brown PetscCall(DMLabelSetStratumBounds(depth, 1, 0, local_elems)); 2113e72e933SJed Brown } else { 2123e72e933SJed Brown PetscCall(DMPlexSymmetrize(dm)); 2133e72e933SJed Brown PetscCall(DMPlexStratify(dm)); 2143e72e933SJed Brown } 2153e72e933SJed Brown { // Create point SF 2163e72e933SJed Brown PetscSF sf; 2173e72e933SJed Brown PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf); 2183e72e933SJed Brown PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z); 2193e72e933SJed Brown if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found 2203e72e933SJed Brown PetscInt num_ghosts = local_verts - owned_verts; // Due to sorting, owned vertices always come first 2213e72e933SJed Brown PetscInt *local_ghosts; 2223e72e933SJed Brown PetscSFNode *ghosts; 2233e72e933SJed Brown PetscCall(PetscMalloc1(num_ghosts, &local_ghosts)); 2243e72e933SJed Brown PetscCall(PetscMalloc1(num_ghosts, &ghosts)); 2253e72e933SJed Brown for (PetscInt i = 0; i < num_ghosts;) { 2263e72e933SJed Brown ZCode z = vert_z[owned_verts + i]; 2273e72e933SJed Brown PetscInt remote_rank = ZCodeFind(z, size + 1, layout.zstarts), remote_count = 0; 2283e72e933SJed Brown if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 2293e72e933SJed Brown // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z) 2303e72e933SJed Brown 2313e72e933SJed Brown // Count the elements on remote_rank 2323e72e933SJed Brown PetscInt remote_elem = 0; 2333e72e933SJed Brown for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) { 2343e72e933SJed Brown Ijk loc = ZCodeSplit(rz); 2353e72e933SJed Brown if (IjkActive(layout.eextent, loc)) remote_elem++; 2363e72e933SJed Brown } 2373e72e933SJed Brown 2383e72e933SJed Brown // Traverse vertices and make ghost links 2393e72e933SJed Brown for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) { 2403e72e933SJed Brown Ijk loc = ZCodeSplit(rz); 2413e72e933SJed Brown if (rz == z) { 2423e72e933SJed Brown local_ghosts[i] = local_elems + owned_verts + i; 2433e72e933SJed Brown ghosts[i].rank = remote_rank; 2443e72e933SJed Brown ghosts[i].index = remote_elem + remote_count; 2453e72e933SJed Brown i++; 2463e72e933SJed Brown if (i == num_ghosts) break; 2473e72e933SJed Brown z = vert_z[owned_verts + i]; 2483e72e933SJed Brown } 2493e72e933SJed Brown if (IjkActive(layout.vextent, loc)) remote_count++; 2503e72e933SJed Brown } 2513e72e933SJed Brown } 2523e72e933SJed Brown PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER)); 2533e72e933SJed Brown PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF")); 2543e72e933SJed Brown PetscCall(DMSetPointSF(dm, sf)); 2553e72e933SJed Brown PetscCall(PetscSFDestroy(&sf)); 2563e72e933SJed Brown } 2573e72e933SJed Brown { 2583e72e933SJed Brown Vec coordinates; 2593e72e933SJed Brown PetscScalar *coords; 2603e72e933SJed Brown PetscSection coord_section; 2613e72e933SJed Brown PetscInt coord_size; 2623e72e933SJed Brown PetscCall(DMGetCoordinateSection(dm, &coord_section)); 2633e72e933SJed Brown PetscCall(PetscSectionSetNumFields(coord_section, 1)); 2643e72e933SJed Brown PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim)); 2653e72e933SJed Brown PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts)); 2663e72e933SJed Brown for (PetscInt v = 0; v < local_verts; v++) { 2673e72e933SJed Brown PetscInt point = local_elems + v; 2683e72e933SJed Brown PetscCall(PetscSectionSetDof(coord_section, point, dim)); 2693e72e933SJed Brown PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim)); 2703e72e933SJed Brown } 2713e72e933SJed Brown PetscCall(PetscSectionSetUp(coord_section)); 2723e72e933SJed Brown PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size)); 2733e72e933SJed Brown PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates)); 2743e72e933SJed Brown PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates")); 2753e72e933SJed Brown PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE)); 2763e72e933SJed Brown PetscCall(VecSetBlockSize(coordinates, dim)); 2773e72e933SJed Brown PetscCall(VecSetType(coordinates, VECSTANDARD)); 2783e72e933SJed Brown PetscCall(VecGetArray(coordinates, &coords)); 2793e72e933SJed Brown for (PetscInt v = 0; v < local_verts; v++) { 2803e72e933SJed Brown Ijk loc = ZCodeSplit(vert_z[v]); 2813e72e933SJed Brown coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i; 2823e72e933SJed Brown if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j; 2833e72e933SJed Brown if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k; 2843e72e933SJed Brown } 2853e72e933SJed Brown PetscCall(VecRestoreArray(coordinates, &coords)); 2863e72e933SJed Brown PetscCall(DMSetCoordinatesLocal(dm, coordinates)); 2873e72e933SJed Brown PetscCall(VecDestroy(&coordinates)); 2883e72e933SJed Brown PetscCall(PetscFree(layout.zstarts)); 2893e72e933SJed Brown } 2903e72e933SJed Brown if (interpolate) { 2913431e603SJed Brown PetscCall(DMPlexInterpolateInPlace_Internal(dm)); 2923431e603SJed Brown 2933431e603SJed Brown DMLabel label; 2943431e603SJed Brown PetscCall(DMCreateLabel(dm, "Face Sets")); 2953431e603SJed Brown PetscCall(DMGetLabel(dm, "Face Sets", &label)); 2963431e603SJed Brown PetscInt fStart, fEnd, vStart, vEnd; 2973431e603SJed Brown PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 2983431e603SJed Brown PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 2993431e603SJed Brown for (PetscInt f = fStart; f < fEnd; f++) { 3003431e603SJed Brown PetscInt npoints; 3013431e603SJed Brown PetscInt *points = NULL; 3023431e603SJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 3033431e603SJed Brown PetscInt bc_count[6] = {0}; 3043431e603SJed Brown for (PetscInt i = 0; i < npoints; i++) { 3053431e603SJed Brown PetscInt p = points[2 * i]; 3063431e603SJed Brown if (p < vStart || vEnd <= p) continue; 3073431e603SJed Brown Ijk loc = ZCodeSplit(vert_z[p - vStart]); 3083431e603SJed Brown // Convention here matches DMPlexCreateCubeMesh_Internal 3093431e603SJed Brown bc_count[0] += loc.i == 0; 3103431e603SJed Brown bc_count[1] += loc.i == layout.vextent.i - 1; 3113431e603SJed Brown bc_count[2] += loc.j == 0; 3123431e603SJed Brown bc_count[3] += loc.j == layout.vextent.j - 1; 3133431e603SJed Brown bc_count[4] += loc.k == 0; 3143431e603SJed Brown bc_count[5] += loc.k == layout.vextent.k - 1; 3153e72e933SJed Brown } 3163431e603SJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 3173431e603SJed Brown for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) { 3183431e603SJed Brown if (bc_count[bc] == PetscPowInt(2, dim - 1)) { 3193431e603SJed Brown PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets"); 3203431e603SJed Brown PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc])); 3213431e603SJed Brown bc_match++; 3223431e603SJed Brown } 3233431e603SJed Brown } 3243431e603SJed Brown } 3253431e603SJed Brown // Ensure that the Coordinate DM has our new boundary labels 3263431e603SJed Brown DM cdm; 3273431e603SJed Brown PetscCall(DMGetCoordinateDM(dm, &cdm)); 3283431e603SJed Brown PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL)); 3293431e603SJed Brown } 3303431e603SJed Brown PetscCall(PetscFree(vert_z)); 3313e72e933SJed Brown PetscFunctionReturn(0); 3323e72e933SJed Brown } 333