Date: 2026-05-01T06:12:12+00:00

Actual source code: tsconvest.c

      1: #include <petscconvest.h>
      2: #include <petscts.h>
      3: #include <petscdmplex.h>

      5: #include <petsc/private/petscconvestimpl.h>

      7: static PetscErrorCode PetscConvEstSetTS_Private(PetscConvEst ce, PetscObject solver)
      8: {
      9:   PetscClassId id;

     11:   PetscFunctionBegin;
     12:   PetscCall(PetscObjectGetClassId(ce->solver, &id));
     13:   PetscCheck(id == TS_CLASSID, PetscObjectComm((PetscObject)ce), PETSC_ERR_ARG_WRONG, "Solver was not a TS");
     14:   PetscCall(TSGetDM((TS)ce->solver, &ce->idm));
     15:   PetscFunctionReturn(PETSC_SUCCESS);
     16: }

     18: static PetscErrorCode PetscConvEstInitGuessTS_Private(PetscConvEst ce, PetscInt r, DM dm, Vec u)
     19: {
     20:   PetscFunctionBegin;
     21:   PetscCall(TSComputeInitialCondition((TS)ce->solver, u));
     22:   PetscFunctionReturn(PETSC_SUCCESS);
     23: }

     25: static PetscErrorCode PetscConvEstComputeErrorTS_Private(PetscConvEst ce, PetscInt r, DM dm, Vec u, PetscReal errors[])
     26: {
     27:   TS ts = (TS)ce->solver;
     28:   PetscErrorCode (*exactError)(TS, Vec, Vec);

     30:   PetscFunctionBegin;
     31:   PetscCall(TSGetComputeExactError(ts, &exactError));
     32:   if (exactError) {
     33:     Vec      e;
     34:     PetscInt f;

     36:     PetscCall(VecDuplicate(u, &e));
     37:     PetscCall(TSComputeExactError(ts, u, e));
     38:     PetscCall(VecNorm(e, NORM_2, errors));
     39:     for (f = 1; f < ce->Nf; ++f) errors[f] = errors[0];
     40:     PetscCall(VecDestroy(&e));
     41:   } else {
     42:     PetscReal t;

     44:     PetscCall(TSGetSolveTime(ts, &t));
     45:     PetscCall(DMComputeL2FieldDiff(dm, t, ce->exactSol, ce->ctxs, u, errors));
     46:   }
     47:   PetscFunctionReturn(PETSC_SUCCESS);
     48: }

     50: static PetscErrorCode PetscConvEstGetConvRateTS_Temporal_Private(PetscConvEst ce, PetscReal alpha[])
     51: {
     52:   TS         ts = (TS)ce->solver;
     53:   Vec        u, u0;
     54:   PetscReal *dt, *x, *y, slope, intercept;
     55:   PetscInt   Ns, oNs, Nf = ce->Nf, f, Nr = ce->Nr, r;

     57:   PetscFunctionBegin;
     58:   PetscCall(PetscMalloc1(Nr + 1, &dt));
     59:   PetscCall(TSGetTimeStep(ts, &dt[0]));
     60:   PetscCall(TSGetMaxSteps(ts, &oNs));
     61:   PetscCall(TSGetSolution(ts, &u0));
     62:   PetscCall(PetscObjectReference((PetscObject)u0));
     63:   Ns = oNs;
     64:   for (r = 0; r <= Nr; ++r) {
     65:     if (r > 0) {
     66:       dt[r] = dt[r - 1] / ce->r;
     67:       Ns    = PetscCeilReal(Ns * ce->r);
     68:     }
     69:     PetscCall(TSSetTime(ts, 0.0));
     70:     PetscCall(TSSetStepNumber(ts, 0));
     71:     PetscCall(TSSetTimeStep(ts, dt[r]));
     72:     PetscCall(TSSetMaxSteps(ts, Ns));
     73:     PetscCall(TSGetSolution(ts, &u));
     74:     PetscCall(PetscConvEstComputeInitialGuess(ce, r, NULL, u));
     75:     PetscCall(TSSolve(ts, NULL));
     76:     PetscCall(TSGetSolution(ts, &u));
     77:     PetscCall(PetscLogEventBegin(ce->event, ce, 0, 0, 0));
     78:     PetscCall(PetscConvEstComputeError(ce, r, ce->idm, u, &ce->errors[r * Nf]));
     79:     PetscCall(PetscLogEventEnd(ce->event, ce, 0, 0, 0));
     80:     for (f = 0; f < Nf; ++f) {
     81:       ce->dofs[r * Nf + f] = 1.0 / dt[r];
     82:       PetscCall(PetscLogEventSetDof(ce->event, f, ce->dofs[r * Nf + f]));
     83:       PetscCall(PetscLogEventSetError(ce->event, f, ce->errors[r * Nf + f]));
     84:     }
     85:     /* Monitor */
     86:     PetscCall(PetscConvEstMonitorDefault(ce, r));
     87:   }
     88:   /* Fit convergence rate */
     89:   if (Nr) {
     90:     PetscCall(PetscMalloc2(Nr + 1, &x, Nr + 1, &y));
     91:     for (f = 0; f < Nf; ++f) {
     92:       for (r = 0; r <= Nr; ++r) {
     93:         x[r] = PetscLog10Real(dt[r]);
     94:         y[r] = PetscLog10Real(ce->errors[r * Nf + f]);
     95:       }
     96:       PetscCall(PetscLinearRegression(Nr + 1, x, y, &slope, &intercept));
     97:       /* Since lg err = s lg dt + b */
     98:       alpha[f] = slope;
     99:     }
    100:     PetscCall(PetscFree2(x, y));
    101:   }
    102:   /* Reset solver */
    103:   PetscCall(TSReset(ts));
    104:   PetscCall(TSSetConvergedReason(ts, TS_CONVERGED_ITERATING));
    105:   PetscCall(TSSetTime(ts, 0.0));
    106:   PetscCall(TSSetStepNumber(ts, 0));
    107:   PetscCall(TSSetTimeStep(ts, dt[0]));
    108:   PetscCall(TSSetMaxSteps(ts, oNs));
    109:   PetscCall(TSSetSolution(ts, u0));
    110:   PetscCall(PetscConvEstComputeInitialGuess(ce, 0, NULL, u0));
    111:   PetscCall(VecDestroy(&u0));
    112:   PetscCall(PetscFree(dt));
    113:   PetscFunctionReturn(PETSC_SUCCESS);
    114: }

    116: static PetscErrorCode PetscConvEstGetConvRateTS_Spatial_Private(PetscConvEst ce, PetscReal alpha[])
    117: {
    118:   TS          ts = (TS)ce->solver;
    119:   Vec         uInitial;
    120:   DM         *dm;
    121:   PetscObject disc;
    122:   PetscReal  *x, *y, slope, intercept;
    123:   PetscInt    Nr = ce->Nr, r, Nf = ce->Nf, f, dim, oldlevel, oldnlev;
    124:   PetscErrorCode (*ifunc)(DM, PetscReal, Vec, Vec, Vec, void *);
    125:   PetscErrorCode (*ijac)(DM, PetscReal, Vec, Vec, PetscReal, Mat, Mat, void *);
    126:   PetscErrorCode (*rhsfunc)(DM, PetscReal, Vec, Vec, void *);
    127:   PetscCtx fctx, jctx, rctx, ctx;

    129:   PetscFunctionBegin;
    130:   PetscCheck(ce->r == 2.0, PetscObjectComm((PetscObject)ce), PETSC_ERR_SUP, "Only refinement factor 2 is currently supported (not %g)", (double)ce->r);
    131:   PetscCall(DMGetDimension(ce->idm, &dim));
    132:   PetscCall(DMGetApplicationContext(ce->idm, &ctx));
    133:   PetscCall(DMPlexSetRefinementUniform(ce->idm, PETSC_TRUE));
    134:   PetscCall(DMGetRefineLevel(ce->idm, &oldlevel));
    135:   PetscCall(PetscMalloc1(Nr + 1, &dm));
    136:   PetscCall(TSGetSolution(ts, &uInitial));
    137:   PetscCall(PetscObjectReference((PetscObject)uInitial));

    139:   /* Loop over meshes */
    140:   dm[0] = ce->idm;
    141:   for (r = 0; r <= Nr; ++r) {
    142:     Vec           u;
    143:     PetscLogStage stage;
    144:     char          stageName[PETSC_MAX_PATH_LEN];
    145:     const char   *dmname, *uname;

    147:     PetscCall(PetscSNPrintf(stageName, PETSC_MAX_PATH_LEN - 1, "ConvEst Refinement Level %" PetscInt_FMT, r));
    148:     PetscCall(PetscLogStageGetId(stageName, &stage));
    149:     if (stage < 0) PetscCall(PetscLogStageRegister(stageName, &stage));
    150:     PetscCall(PetscLogStagePush(stage));
    151:     if (r > 0) {
    152:       if (!ce->noRefine) {
    153:         PetscCall(DMRefine(dm[r - 1], MPI_COMM_NULL, &dm[r]));
    154:         PetscCall(DMSetCoarseDM(dm[r], dm[r - 1]));
    155:       } else {
    156:         DM cdm, rcdm;

    158:         PetscCall(DMClone(dm[r - 1], &dm[r]));
    159:         PetscCall(DMCopyDisc(dm[r - 1], dm[r]));
    160:         PetscCall(DMGetCoordinateDM(dm[r - 1], &cdm));
    161:         PetscCall(DMGetCoordinateDM(dm[r], &rcdm));
    162:         PetscCall(DMCopyDisc(cdm, rcdm));
    163:       }
    164:       PetscCall(DMCopyTransform(ce->idm, dm[r]));
    165:       PetscCall(PetscObjectGetName((PetscObject)dm[r - 1], &dmname));
    166:       PetscCall(PetscObjectSetName((PetscObject)dm[r], dmname));
    167:       for (f = 0; f < Nf; ++f) {
    168:         PetscErrorCode (*nspconstr)(DM, PetscInt, PetscInt, MatNullSpace *);

    170:         PetscCall(DMGetNullSpaceConstructor(dm[r - 1], f, &nspconstr));
    171:         PetscCall(DMSetNullSpaceConstructor(dm[r], f, nspconstr));
    172:       }
    173:     }
    174:     PetscCall(DMViewFromOptions(dm[r], NULL, "-conv_dm_view"));
    175:     /* Create solution */
    176:     PetscCall(DMCreateGlobalVector(dm[r], &u));
    177:     PetscCall(DMGetField(dm[r], 0, NULL, &disc));
    178:     PetscCall(PetscObjectGetName(disc, &uname));
    179:     PetscCall(PetscObjectSetName((PetscObject)u, uname));
    180:     /* Setup solver */
    181:     PetscCall(TSReset(ts));
    182:     PetscCall(TSSetDM(ts, dm[r]));
    183:     PetscCall(DMTSSetBoundaryLocal(dm[r], DMPlexTSComputeBoundary, ctx));
    184:     if (r > 0) {
    185:       PetscCall(DMTSGetIFunctionLocal(dm[r - 1], &ifunc, &fctx));
    186:       PetscCall(DMTSGetIJacobianLocal(dm[r - 1], &ijac, &jctx));
    187:       PetscCall(DMTSGetRHSFunctionLocal(dm[r - 1], &rhsfunc, &rctx));
    188:       if (ifunc) PetscCall(DMTSSetIFunctionLocal(dm[r], ifunc, fctx));
    189:       if (ijac) PetscCall(DMTSSetIJacobianLocal(dm[r], ijac, jctx));
    190:       if (rhsfunc) PetscCall(DMTSSetRHSFunctionLocal(dm[r], rhsfunc, rctx));
    191:     }
    192:     PetscCall(TSSetTime(ts, 0.0));
    193:     PetscCall(TSSetStepNumber(ts, 0));
    194:     PetscCall(TSSetFromOptions(ts));
    195:     PetscCall(TSSetSolution(ts, u));
    196:     PetscCall(VecDestroy(&u));
    197:     /* Create initial guess */
    198:     PetscCall(TSGetSolution(ts, &u));
    199:     PetscCall(PetscConvEstComputeInitialGuess(ce, r, dm[r], u));
    200:     PetscCall(TSSolve(ts, NULL));
    201:     PetscCall(TSGetSolution(ts, &u));
    202:     PetscCall(PetscLogEventBegin(ce->event, ce, 0, 0, 0));
    203:     PetscCall(PetscConvEstComputeError(ce, r, dm[r], u, &ce->errors[r * Nf]));
    204:     PetscCall(PetscLogEventEnd(ce->event, ce, 0, 0, 0));
    205:     for (f = 0; f < Nf; ++f) {
    206:       PetscSection s, fs;
    207:       PetscInt     lsize;

    209:       /* Could use DMGetOutputDM() to add in Dirichlet dofs */
    210:       PetscCall(DMGetLocalSection(dm[r], &s));
    211:       PetscCall(PetscSectionGetField(s, f, &fs));
    212:       PetscCall(PetscSectionGetConstrainedStorageSize(fs, &lsize));
    213:       PetscCallMPI(MPIU_Allreduce(&lsize, &ce->dofs[r * Nf + f], 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)ts)));
    214:       PetscCall(PetscLogEventSetDof(ce->event, f, ce->dofs[r * Nf + f]));
    215:       PetscCall(PetscLogEventSetError(ce->event, f, ce->errors[r * Nf + f]));
    216:     }
    217:     /* Monitor */
    218:     PetscCall(PetscConvEstMonitorDefault(ce, r));
    219:     if (!r) {
    220:       /* PCReset() does not wipe out the level structure */
    221:       SNES snes;
    222:       KSP  ksp;
    223:       PC   pc;

    225:       PetscCall(TSGetSNES(ts, &snes));
    226:       PetscCall(SNESGetKSP(snes, &ksp));
    227:       PetscCall(KSPGetPC(ksp, &pc));
    228:       PetscCall(PCMGGetLevels(pc, &oldnlev));
    229:     }
    230:     /* Cleanup */
    231:     PetscCall(PetscLogStagePop());
    232:   }
    233:   PetscCall(DMTSGetIFunctionLocal(dm[r - 1], &ifunc, &fctx));
    234:   PetscCall(DMTSGetIJacobianLocal(dm[r - 1], &ijac, &jctx));
    235:   PetscCall(DMTSGetRHSFunctionLocal(dm[r - 1], &rhsfunc, &rctx));
    236:   for (r = 1; r <= Nr; ++r) PetscCall(DMDestroy(&dm[r]));
    237:   /* Fit convergence rate */
    238:   PetscCall(PetscMalloc2(Nr + 1, &x, Nr + 1, &y));
    239:   for (f = 0; f < Nf; ++f) {
    240:     for (r = 0; r <= Nr; ++r) {
    241:       x[r] = PetscLog10Real(ce->dofs[r * Nf + f]);
    242:       y[r] = PetscLog10Real(ce->errors[r * Nf + f]);
    243:     }
    244:     PetscCall(PetscLinearRegression(Nr + 1, x, y, &slope, &intercept));
    245:     /* Since h^{-dim} = N, lg err = s lg N + b = -s dim lg h + b */
    246:     alpha[f] = -slope * dim;
    247:   }
    248:   PetscCall(PetscFree2(x, y));
    249:   PetscCall(PetscFree(dm));
    250:   /* Restore solver */
    251:   PetscCall(TSReset(ts));
    252:   {
    253:     /* PCReset() does not wipe out the level structure */
    254:     SNES snes;
    255:     KSP  ksp;
    256:     PC   pc;

    258:     PetscCall(TSGetSNES(ts, &snes));
    259:     PetscCall(SNESGetKSP(snes, &ksp));
    260:     PetscCall(KSPGetPC(ksp, &pc));
    261:     PetscCall(PCMGSetLevels(pc, oldnlev, NULL));
    262:     PetscCall(DMSetRefineLevel(ce->idm, oldlevel)); /* The damn DMCoarsen() calls in PCMG can reset this */
    263:   }
    264:   PetscCall(TSSetDM(ts, ce->idm));
    265:   PetscCall(DMTSSetBoundaryLocal(ce->idm, DMPlexTSComputeBoundary, ctx));
    266:   if (ifunc) PetscCall(DMTSSetIFunctionLocal(ce->idm, ifunc, fctx));
    267:   if (ijac) PetscCall(DMTSSetIJacobianLocal(ce->idm, ijac, jctx));
    268:   if (rhsfunc) PetscCall(DMTSSetRHSFunctionLocal(ce->idm, rhsfunc, rctx));
    269:   PetscCall(TSSetConvergedReason(ts, TS_CONVERGED_ITERATING));
    270:   PetscCall(TSSetTime(ts, 0.0));
    271:   PetscCall(TSSetStepNumber(ts, 0));
    272:   PetscCall(TSSetFromOptions(ts));
    273:   PetscCall(TSSetSolution(ts, uInitial));
    274:   PetscCall(PetscConvEstComputeInitialGuess(ce, 0, NULL, uInitial));
    275:   PetscCall(VecDestroy(&uInitial));
    276:   PetscFunctionReturn(PETSC_SUCCESS);
    277: }

    279: PetscErrorCode PetscConvEstUseTS(PetscConvEst ce, PetscBool checkTemporal)
    280: {
    281:   PetscFunctionBegin;
    283:   ce->ops->setsolver    = PetscConvEstSetTS_Private;
    284:   ce->ops->initguess    = PetscConvEstInitGuessTS_Private;
    285:   ce->ops->computeerror = PetscConvEstComputeErrorTS_Private;
    286:   if (checkTemporal) {
    287:     ce->ops->getconvrate = PetscConvEstGetConvRateTS_Temporal_Private;
    288:   } else {
    289:     ce->ops->getconvrate = PetscConvEstGetConvRateTS_Spatial_Private;
    290:   }
    291:   PetscFunctionReturn(PETSC_SUCCESS);
    292: }