Actual source code: dmperiodicity.c

      1: #include <petsc/private/dmimpl.h>

      3: #include <petscdmplex.h>

      5: /*@C
      6:   DMGetPeriodicity - Get the description of mesh periodicity

      8:   Not collective

     10:   Input Parameter:
     11: . dm - The `DM` object

     13:   Output Parameters:
     14: + maxCell - Over distances greater than this, we can assume a point has crossed over to another sheet, when trying to localize cell coordinates
     15: . Lstart  - If we assume the mesh is a torus, this is the start of each coordinate, or `NULL` for 0.0
     16: - L       - If we assume the mesh is a torus, this is the length of each coordinate, otherwise it is < 0.0

     18:   Level: developer

     20: .seealso: `DM`
     21: @*/
     22: PetscErrorCode DMGetPeriodicity(DM dm, const PetscReal *maxCell[], const PetscReal *Lstart[], const PetscReal *L[])
     23: {
     24:   PetscFunctionBegin;
     26:   if (maxCell) *maxCell = dm->maxCell;
     27:   if (Lstart) *Lstart = dm->Lstart;
     28:   if (L) *L = dm->L;
     29:   PetscFunctionReturn(PETSC_SUCCESS);
     30: }

     32: /*@
     33:   DMSetPeriodicity - Set the description of mesh periodicity

     35:   Logically Collective

     37:   Input Parameters:
     38: + dm      - The `DM` object
     39: . maxCell - Over distances greater than this, we can assume a point has crossed over to another sheet, when trying to localize cell coordinates. Pass `NULL` to remove such information.
     40: . Lstart  - If we assume the mesh is a torus, this is the start of each coordinate, or `NULL` for 0.0
     41: - L       - If we assume the mesh is a torus, this is the length of each coordinate, otherwise it is < 0.0

     43:   Level: developer

     45: .seealso: `DM`, `DMGetPeriodicity()`
     46: @*/
     47: PetscErrorCode DMSetPeriodicity(DM dm, const PetscReal maxCell[], const PetscReal Lstart[], const PetscReal L[])
     48: {
     49:   PetscInt dim, d;

     51:   PetscFunctionBegin;
     53:   if (maxCell) PetscAssertPointer(maxCell, 2);
     54:   if (Lstart) PetscAssertPointer(Lstart, 3);
     55:   if (L) PetscAssertPointer(L, 4);
     56:   PetscCall(DMGetDimension(dm, &dim));
     57:   if (maxCell) {
     58:     if (!dm->maxCell) PetscCall(PetscMalloc1(dim, &dm->maxCell));
     59:     for (d = 0; d < dim; ++d) dm->maxCell[d] = maxCell[d];
     60:   } else { /* remove maxCell information to disable automatic computation of localized vertices */
     61:     PetscCall(PetscFree(dm->maxCell));
     62:     dm->maxCell = NULL;
     63:   }
     64:   if (Lstart) {
     65:     if (!dm->Lstart) PetscCall(PetscMalloc1(dim, &dm->Lstart));
     66:     for (d = 0; d < dim; ++d) dm->Lstart[d] = Lstart[d];
     67:   } else { /* remove L information to disable automatic computation of localized vertices */
     68:     PetscCall(PetscFree(dm->Lstart));
     69:     dm->Lstart = NULL;
     70:   }
     71:   if (L) {
     72:     if (!dm->L) PetscCall(PetscMalloc1(dim, &dm->L));
     73:     for (d = 0; d < dim; ++d) dm->L[d] = L[d];
     74:   } else { /* remove L information to disable automatic computation of localized vertices */
     75:     PetscCall(PetscFree(dm->L));
     76:     dm->L = NULL;
     77:   }
     78:   PetscCheck((dm->maxCell && dm->L) || (!dm->maxCell && !dm->L), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot set only one of maxCell/L");
     79:   PetscFunctionReturn(PETSC_SUCCESS);
     80: }

     82: /*@
     83:   DMLocalizeCoordinate - If a mesh is periodic (a torus with lengths L_i, some of which can be infinite), project the coordinate onto [0, L_i) in each dimension.

     85:   Input Parameters:
     86: + dm       - The `DM`
     87: . in       - The input coordinate point (dim numbers)
     88: - endpoint - Include the endpoint L_i

     90:   Output Parameter:
     91: . out - The localized coordinate point (dim numbers)

     93:   Level: developer

     95: .seealso: `DM`, `DMLocalizeCoordinates()`, `DMLocalizeAddCoordinate()`
     96: @*/
     97: PetscErrorCode DMLocalizeCoordinate(DM dm, const PetscScalar in[], PetscBool endpoint, PetscScalar out[])
     98: {
     99:   PetscInt dim, d;

    101:   PetscFunctionBegin;
    102:   PetscCall(DMGetCoordinateDim(dm, &dim));
    103:   if (!dm->maxCell) {
    104:     for (d = 0; d < dim; ++d) out[d] = in[d];
    105:   } else {
    106:     if (endpoint) {
    107:       for (d = 0; d < dim; ++d) {
    108:         if ((PetscAbsReal(PetscRealPart(in[d]) / dm->L[d] - PetscFloorReal(PetscRealPart(in[d]) / dm->L[d])) < PETSC_SMALL) && (PetscRealPart(in[d]) / dm->L[d] > PETSC_SMALL)) {
    109:           out[d] = in[d] - dm->L[d] * (PetscFloorReal(PetscRealPart(in[d]) / dm->L[d]) - 1);
    110:         } else {
    111:           out[d] = in[d] - dm->L[d] * PetscFloorReal(PetscRealPart(in[d]) / dm->L[d]);
    112:         }
    113:       }
    114:     } else {
    115:       for (d = 0; d < dim; ++d) out[d] = in[d] - dm->L[d] * PetscFloorReal(PetscRealPart(in[d]) / dm->L[d]);
    116:     }
    117:   }
    118:   PetscFunctionReturn(PETSC_SUCCESS);
    119: }

    121: /*
    122:   DMLocalizeCoordinate_Internal - If a mesh is periodic, and the input point is far from the anchor, pick the coordinate sheet of the torus which moves it closer.

    124:   Input Parameters:
    125: + dm     - The `DM`
    126: . dim    - The spatial dimension
    127: . anchor - The anchor point, the input point can be no more than maxCell away from it
    128: - in     - The input coordinate point (dim numbers)

    130:   Output Parameter:
    131: . out - The localized coordinate point (dim numbers)

    133:   Level: developer

    135:   Note:
    136:   This is meant to get a set of coordinates close to each other, as in a cell. The anchor is usually the one of the vertices on a containing cell

    138: .seealso: `DM`, `DMLocalizeCoordinates()`, `DMLocalizeAddCoordinate()`
    139: */
    140: PetscErrorCode DMLocalizeCoordinate_Internal(DM dm, PetscInt dim, const PetscScalar anchor[], const PetscScalar in[], PetscScalar out[])
    141: {
    142:   PetscInt d;

    144:   PetscFunctionBegin;
    145:   if (!dm->maxCell) {
    146:     for (d = 0; d < dim; ++d) out[d] = in[d];
    147:   } else {
    148:     for (d = 0; d < dim; ++d) {
    149:       if ((dm->L[d] > 0.0) && (PetscAbsScalar(anchor[d] - in[d]) > dm->maxCell[d])) {
    150:         out[d] = PetscRealPart(anchor[d]) > PetscRealPart(in[d]) ? dm->L[d] + in[d] : in[d] - dm->L[d];
    151:       } else {
    152:         out[d] = in[d];
    153:       }
    154:     }
    155:   }
    156:   PetscFunctionReturn(PETSC_SUCCESS);
    157: }

    159: PetscErrorCode DMLocalizeCoordinateReal_Internal(DM dm, PetscInt dim, const PetscReal anchor[], const PetscReal in[], PetscReal out[])
    160: {
    161:   PetscInt d;

    163:   PetscFunctionBegin;
    164:   if (!dm->maxCell) {
    165:     for (d = 0; d < dim; ++d) out[d] = in[d];
    166:   } else {
    167:     for (d = 0; d < dim; ++d) {
    168:       if ((dm->L[d] > 0.0) && (PetscAbsReal(anchor[d] - in[d]) > dm->maxCell[d])) {
    169:         out[d] = anchor[d] > in[d] ? dm->L[d] + in[d] : in[d] - dm->L[d];
    170:       } else {
    171:         out[d] = in[d];
    172:       }
    173:     }
    174:   }
    175:   PetscFunctionReturn(PETSC_SUCCESS);
    176: }

    178: /*
    179:   DMLocalizeAddCoordinate_Internal - If a mesh is periodic, and the input point is far from the anchor, pick the coordinate sheet of the torus which moves it closer.

    181:   Input Parameters:
    182: + dm     - The `DM`
    183: . dim    - The spatial dimension
    184: . anchor - The anchor point, the input point can be no more than maxCell away from it
    185: . in     - The input coordinate delta (dim numbers)
    186: - out    - The input coordinate point (dim numbers)

    188:   Output Parameter:
    189: . out    - The localized coordinate in + out

    191:   Level: developer

    193:   Note:
    194:   This is meant to get a set of coordinates close to each other, as in a cell. The anchor is usually one of the vertices on a containing cell

    196: .seealso: `DM`, `DMLocalizeCoordinates()`, `DMLocalizeCoordinate()`
    197: */
    198: PetscErrorCode DMLocalizeAddCoordinate_Internal(DM dm, PetscInt dim, const PetscScalar anchor[], const PetscScalar in[], PetscScalar out[])
    199: {
    200:   PetscInt d;

    202:   PetscFunctionBegin;
    203:   if (!dm->maxCell) {
    204:     for (d = 0; d < dim; ++d) out[d] += in[d];
    205:   } else {
    206:     for (d = 0; d < dim; ++d) {
    207:       const PetscReal maxC = dm->maxCell[d];

    209:       if ((dm->L[d] > 0.0) && (PetscAbsScalar(anchor[d] - in[d]) > maxC)) {
    210:         const PetscScalar newCoord = PetscRealPart(anchor[d]) > PetscRealPart(in[d]) ? dm->L[d] + in[d] : in[d] - dm->L[d];

    212:         if (PetscAbsScalar(newCoord - anchor[d]) > maxC)
    213:           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "%" PetscInt_FMT "-Coordinate %g more than %g away from anchor %g", d, (double)PetscRealPart(in[d]), (double)maxC, (double)PetscRealPart(anchor[d]));
    214:         out[d] += newCoord;
    215:       } else {
    216:         out[d] += in[d];
    217:       }
    218:     }
    219:   }
    220:   PetscFunctionReturn(PETSC_SUCCESS);
    221: }

    223: /*@
    224:   DMGetCoordinatesLocalizedLocal - Check if the `DM` coordinates have been localized for cells on this process

    226:   Not Collective

    228:   Input Parameter:
    229: . dm - The `DM`

    231:   Output Parameter:
    232: . areLocalized - `PETSC_TRUE` if localized

    234:   Level: developer

    236: .seealso: `DM`, `DMLocalizeCoordinates()`, `DMGetCoordinatesLocalized()`, `DMSetPeriodicity()`
    237: @*/
    238: PetscErrorCode DMGetCoordinatesLocalizedLocal(DM dm, PetscBool *areLocalized)
    239: {
    240:   PetscFunctionBegin;
    242:   PetscAssertPointer(areLocalized, 2);
    243:   *areLocalized = dm->coordinates[1].dim < 0 ? PETSC_FALSE : PETSC_TRUE;
    244:   PetscFunctionReturn(PETSC_SUCCESS);
    245: }

    247: /*@
    248:   DMGetCoordinatesLocalized - Check if the `DM` coordinates have been localized for cells

    250:   Collective

    252:   Input Parameter:
    253: . dm - The `DM`

    255:   Output Parameter:
    256: . areLocalized - `PETSC_TRUE` if localized

    258:   Level: developer

    260: .seealso: `DM`, `DMLocalizeCoordinates()`, `DMSetPeriodicity()`, `DMGetCoordinatesLocalizedLocal()`
    261: @*/
    262: PetscErrorCode DMGetCoordinatesLocalized(DM dm, PetscBool *areLocalized)
    263: {
    264:   PetscBool localized;

    266:   PetscFunctionBegin;
    268:   PetscAssertPointer(areLocalized, 2);
    269:   PetscCall(DMGetCoordinatesLocalizedLocal(dm, &localized));
    270:   PetscCallMPI(MPIU_Allreduce(&localized, areLocalized, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
    271:   PetscFunctionReturn(PETSC_SUCCESS);
    272: }

    274: /*@
    275:   DMGetSparseLocalize - Check if the `DM` coordinates should be localized only for cells near the periodic boundary.

    277:   Not collective

    279:   Input Parameter:
    280: . dm - The `DM`

    282:   Output Parameter:
    283: . sparse - `PETSC_TRUE` if only cells near the periodic boundary are localized

    285:   Level: intermediate

    287: .seealso: `DMSetSparseLocalize()`, `DMLocalizeCoordinates()`, `DMSetPeriodicity()`
    288: @*/
    289: PetscErrorCode DMGetSparseLocalize(DM dm, PetscBool *sparse)
    290: {
    291:   PetscFunctionBegin;
    293:   PetscAssertPointer(sparse, 2);
    294:   *sparse = dm->sparseLocalize;
    295:   PetscFunctionReturn(PETSC_SUCCESS);
    296: }

    298: /*@
    299:   DMSetSparseLocalize - Set the flag indicating that `DM` coordinates should be localized only for cells near the periodic boundary.

    301:   Logically collective

    303:   Input Parameters:
    304: + dm     - The `DM`
    305: - sparse - `PETSC_TRUE` if only cells near the periodic boundary are localized

    307:   Level: intermediate

    309: .seealso: `DMGetSparseLocalize()`, `DMLocalizeCoordinates()`, `DMSetPeriodicity()`
    310: @*/
    311: PetscErrorCode DMSetSparseLocalize(DM dm, PetscBool sparse)
    312: {
    313:   PetscFunctionBegin;
    316:   dm->sparseLocalize = sparse;
    317:   PetscFunctionReturn(PETSC_SUCCESS);
    318: }

    320: /*@
    321:   DMLocalizeCoordinates - If a mesh is periodic, create local coordinates for cells having periodic faces

    323:   Collective

    325:   Input Parameter:
    326: . dm - The `DM`

    328:   Level: developer

    330: .seealso: `DM`, `DMSetPeriodicity()`, `DMLocalizeCoordinate()`, `DMLocalizeAddCoordinate()`
    331: @*/
    332: PetscErrorCode DMLocalizeCoordinates(DM dm)
    333: {
    334:   DM               cdm, cdgdm, cplex, plex;
    335:   PetscSection     cs, csDG;
    336:   Vec              coordinates, cVec;
    337:   PetscScalar     *coordsDG, *anchor, *localized;
    338:   const PetscReal *Lstart, *L;
    339:   PetscInt         Nc, vStart, vEnd, sStart, sEnd, newStart = PETSC_INT_MAX, newEnd = PETSC_INT_MIN, bs, coordSize;
    340:   PetscBool        isLocalized, sparseLocalize, useDG = PETSC_FALSE, useDGGlobal;
    341:   PetscInt         maxHeight = 0, h;
    342:   PetscInt        *pStart = NULL, *pEnd = NULL;
    343:   MPI_Comm         comm;

    345:   PetscFunctionBegin;
    347:   PetscCall(DMGetPeriodicity(dm, NULL, &Lstart, &L));
    348:   PetscCall(DMGetSparseLocalize(dm, &sparseLocalize));
    349:   /* Cannot automatically localize without L and maxCell right now */
    350:   if (!L) PetscFunctionReturn(PETSC_SUCCESS);
    351:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    352:   PetscCall(DMGetCoordinatesLocalized(dm, &isLocalized));
    353:   if (isLocalized) PetscFunctionReturn(PETSC_SUCCESS);

    355:   PetscCall(DMGetCoordinateDM(dm, &cdm));
    356:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    357:   PetscCall(DMConvert(cdm, DMPLEX, &cplex));
    358:   PetscCheck(cplex, comm, PETSC_ERR_ARG_WRONG, "Coordinate localization requires a DMPLEX coordinate DM");
    359:   PetscCall(DMPlexGetDepthStratum(cplex, 0, &vStart, &vEnd));
    360:   PetscCall(DMPlexGetMaxProjectionHeight(cplex, &maxHeight));
    361:   PetscCall(DMGetWorkArray(dm, 2 * (maxHeight + 1), MPIU_INT, &pStart));
    362:   pEnd     = &pStart[maxHeight + 1];
    363:   newStart = vStart;
    364:   newEnd   = vEnd;
    365:   for (h = 0; h <= maxHeight; h++) {
    366:     PetscCall(DMPlexGetHeightStratum(cplex, h, &pStart[h], &pEnd[h]));
    367:     newStart = PetscMin(newStart, pStart[h]);
    368:     newEnd   = PetscMax(newEnd, pEnd[h]);
    369:   }
    370:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    371:   PetscCheck(coordinates, comm, PETSC_ERR_SUP, "Missing local coordinate vector");
    372:   PetscCall(DMGetCoordinateSection(dm, &cs));
    373:   PetscCall(VecGetBlockSize(coordinates, &bs));
    374:   PetscCall(PetscSectionGetChart(cs, &sStart, &sEnd));

    376:   PetscCall(PetscSectionCreate(comm, &csDG));
    377:   PetscCall(PetscSectionSetNumFields(csDG, 1));
    378:   PetscCall(PetscSectionGetFieldComponents(cs, 0, &Nc));
    379:   PetscCall(PetscSectionSetFieldComponents(csDG, 0, Nc));
    380:   PetscCall(PetscSectionSetChart(csDG, newStart, newEnd));
    381:   PetscCheck(bs == Nc, comm, PETSC_ERR_ARG_INCOMP, "Coordinate block size %" PetscInt_FMT " != %" PetscInt_FMT " number of components", bs, Nc);

    383:   PetscCall(DMGetWorkArray(dm, 2 * Nc, MPIU_SCALAR, &anchor));
    384:   localized = &anchor[Nc];
    385:   for (h = 0; h <= maxHeight; h++) {
    386:     PetscInt cStart = pStart[h], cEnd = pEnd[h], c;

    388:     for (c = cStart; c < cEnd; ++c) {
    389:       PetscScalar   *cellCoords = NULL;
    390:       DMPolytopeType ct;
    391:       PetscInt       dof, d, p;

    393:       PetscCall(DMPlexGetCellType(plex, c, &ct));
    394:       if (ct == DM_POLYTOPE_FV_GHOST) continue;
    395:       PetscCall(DMPlexVecGetClosure(cplex, cs, coordinates, c, &dof, &cellCoords));
    396:       PetscCheck(!(dof % Nc), comm, PETSC_ERR_ARG_INCOMP, "Coordinate size on cell %" PetscInt_FMT " closure %" PetscInt_FMT " not divisible by %" PetscInt_FMT " number of components", c, dof, Nc);
    397:       for (d = 0; d < Nc; ++d) anchor[d] = cellCoords[d];
    398:       for (p = 0; p < dof / Nc; ++p) {
    399:         PetscCall(DMLocalizeCoordinate_Internal(dm, Nc, anchor, &cellCoords[p * Nc], localized));
    400:         for (d = 0; d < Nc; ++d)
    401:           if (cellCoords[p * Nc + d] != localized[d]) break;
    402:         if (d < Nc) break;
    403:       }
    404:       if (p < dof / Nc) useDG = PETSC_TRUE;
    405:       if (p < dof / Nc || !sparseLocalize) {
    406:         PetscCall(PetscSectionSetDof(csDG, c, dof));
    407:         PetscCall(PetscSectionSetFieldDof(csDG, c, 0, dof));
    408:       }
    409:       PetscCall(DMPlexVecRestoreClosure(cplex, cs, coordinates, c, &dof, &cellCoords));
    410:     }
    411:   }
    412:   PetscCallMPI(MPIU_Allreduce(&useDG, &useDGGlobal, 1, MPI_C_BOOL, MPI_LOR, comm));
    413:   if (!useDGGlobal) goto end;

    415:   PetscCall(PetscSectionSetUp(csDG));
    416:   PetscCall(PetscSectionGetStorageSize(csDG, &coordSize));
    417:   PetscCall(VecCreate(PETSC_COMM_SELF, &cVec));
    418:   PetscCall(PetscObjectSetName((PetscObject)cVec, "coordinates"));
    419:   PetscCall(VecSetBlockSize(cVec, bs));
    420:   PetscCall(VecSetSizes(cVec, coordSize, PETSC_DETERMINE));
    421:   PetscCall(VecSetType(cVec, VECSTANDARD));
    422:   PetscCall(VecGetArray(cVec, &coordsDG));
    423:   for (h = 0; h <= maxHeight; h++) {
    424:     PetscInt cStart = pStart[h], cEnd = pEnd[h], c;

    426:     for (c = cStart; c < cEnd; ++c) {
    427:       PetscScalar *cellCoords = NULL;
    428:       PetscInt     p          = 0, q, dof, cdof, d, offDG;

    430:       PetscCall(PetscSectionGetDof(csDG, c, &cdof));
    431:       if (!cdof) continue;
    432:       PetscCall(DMPlexVecGetClosure(cplex, cs, coordinates, c, &dof, &cellCoords));
    433:       PetscCall(PetscSectionGetOffset(csDG, c, &offDG));
    434:       // TODO The coordinates are set in closure order, which might not be the tensor order
    435:       for (q = 0; q < dof / Nc; ++q) {
    436:         // Select a trial anchor
    437:         for (d = 0; d < Nc; ++d) anchor[d] = cellCoords[q * Nc + d];
    438:         for (p = 0; p < dof / Nc; ++p) {
    439:           PetscCall(DMLocalizeCoordinate_Internal(dm, Nc, anchor, &cellCoords[p * Nc], &coordsDG[offDG + p * Nc]));
    440:           // We need the cell to fit into the torus [lower, lower+L)
    441:           for (d = 0; d < Nc; ++d)
    442:             if (L[d] > 0. && ((PetscRealPart(coordsDG[offDG + p * Nc + d]) < (Lstart ? Lstart[d] : 0.)) || (PetscRealPart(coordsDG[offDG + p * Nc + d]) > (Lstart ? Lstart[d] : 0.) + L[d]))) break;
    443:           if (d < Nc) break;
    444:         }
    445:         if (p == dof / Nc) break;
    446:       }
    447:       PetscCheck(p == dof / Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " does not fit into the torus %s[0, L]", c, Lstart ? "Lstart + " : "");
    448:       PetscCall(DMPlexVecRestoreClosure(cplex, cs, coordinates, c, &dof, &cellCoords));
    449:     }
    450:   }
    451:   PetscCall(VecRestoreArray(cVec, &coordsDG));
    452:   PetscUseTypeMethod(dm, createcellcoordinatedm, &cdgdm);
    453:   PetscCall(DMSetCellCoordinateDM(dm, cdgdm));
    454:   PetscCall(DMDestroy(&cdgdm));
    455:   // Convert the discretization
    456:   {
    457:     PetscFE      fe;
    458:     PetscClassId id;

    460:     PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
    461:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
    462:     if (id == PETSCFE_CLASSID) {
    463:       PetscSpace P;
    464:       PetscInt   degree;

    466:       PetscCall(PetscFEGetBasisSpace(fe, &P));
    467:       PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
    468:       PetscCall(DMPlexCreateCoordinateSpace(dm, degree, PETSC_TRUE, PETSC_FALSE));
    469:     }
    470:   }
    471:   PetscCall(DMSetCellCoordinateSection(dm, PETSC_DETERMINE, csDG));
    472:   PetscCall(DMSetCellCoordinatesLocal(dm, cVec));
    473:   PetscCall(VecDestroy(&cVec));

    475: end:
    476:   PetscCall(DMRestoreWorkArray(dm, 2 * bs, MPIU_SCALAR, &anchor));
    477:   PetscCall(DMRestoreWorkArray(dm, 2 * (maxHeight + 1), MPIU_INT, &pStart));
    478:   PetscCall(PetscSectionDestroy(&csDG));
    479:   PetscCall(DMDestroy(&plex));
    480:   PetscCall(DMDestroy(&cplex));
    481:   PetscFunctionReturn(PETSC_SUCCESS);
    482: }