Actual source code: dmlocalsnes.c

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

      4: typedef struct {
      5:   PetscErrorCode (*objectivelocal)(DM, Vec, PetscReal *, PetscCtx);
      6:   PetscErrorCode (*residuallocal)(DM, Vec, Vec, PetscCtx);
      7:   PetscErrorCode (*jacobianlocal)(DM, Vec, Mat, Mat, PetscCtx);
      8:   PetscErrorCode (*boundarylocal)(DM, Vec, PetscCtx);
      9:   PetscCtx objectivelocalctx;
     10:   PetscCtx residuallocalctx;
     11:   PetscCtx jacobianlocalctx;
     12:   PetscCtx boundarylocalctx;
     13: } DMSNES_Local;

     15: static PetscErrorCode DMSNESDestroy_DMLocal(DMSNES sdm)
     16: {
     17:   PetscFunctionBegin;
     18:   PetscCall(PetscFree(sdm->data));
     19:   sdm->data = NULL;
     20:   PetscFunctionReturn(PETSC_SUCCESS);
     21: }

     23: static PetscErrorCode DMSNESDuplicate_DMLocal(DMSNES oldsdm, DMSNES sdm)
     24: {
     25:   PetscFunctionBegin;
     26:   if (sdm->data != oldsdm->data) {
     27:     PetscCall(PetscFree(sdm->data));
     28:     PetscCall(PetscNew((DMSNES_Local **)&sdm->data));
     29:     if (oldsdm->data) PetscCall(PetscMemcpy(sdm->data, oldsdm->data, sizeof(DMSNES_Local)));
     30:   }
     31:   PetscFunctionReturn(PETSC_SUCCESS);
     32: }

     34: static PetscErrorCode DMLocalSNESGetContext(DM dm, DMSNES sdm, DMSNES_Local **dmlocalsnes)
     35: {
     36:   PetscFunctionBegin;
     37:   *dmlocalsnes = NULL;
     38:   if (!sdm->data) {
     39:     PetscCall(PetscNew((DMSNES_Local **)&sdm->data));

     41:     sdm->ops->destroy   = DMSNESDestroy_DMLocal;
     42:     sdm->ops->duplicate = DMSNESDuplicate_DMLocal;
     43:   }
     44:   *dmlocalsnes = (DMSNES_Local *)sdm->data;
     45:   PetscFunctionReturn(PETSC_SUCCESS);
     46: }

     48: static PetscErrorCode SNESComputeObjective_DMLocal(SNES snes, Vec X, PetscReal *obj, PetscCtx ctx)
     49: {
     50:   DMSNES_Local *dmlocalsnes = (DMSNES_Local *)ctx;
     51:   DM            dm;
     52:   Vec           Xloc;
     53:   PetscBool     transform;

     55:   PetscFunctionBegin;
     58:   PetscCall(SNESGetDM(snes, &dm));
     59:   PetscCall(DMGetLocalVector(dm, &Xloc));
     60:   PetscCall(VecZeroEntries(Xloc));
     61:   /* Non-conforming routines needs boundary values before G2L */
     62:   if (dmlocalsnes->boundarylocal) PetscCall((*dmlocalsnes->boundarylocal)(dm, Xloc, dmlocalsnes->boundarylocalctx));
     63:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, Xloc));
     64:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, Xloc));
     65:   /* Need to reset boundary values if we transformed */
     66:   PetscCall(DMHasBasisTransform(dm, &transform));
     67:   if (transform && dmlocalsnes->boundarylocal) PetscCall((*dmlocalsnes->boundarylocal)(dm, Xloc, dmlocalsnes->boundarylocalctx));
     68:   CHKMEMQ;
     69:   PetscCall((*dmlocalsnes->objectivelocal)(dm, Xloc, obj, dmlocalsnes->objectivelocalctx));
     70:   CHKMEMQ;
     71:   PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, obj, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)snes)));
     72:   PetscCall(DMRestoreLocalVector(dm, &Xloc));
     73:   PetscFunctionReturn(PETSC_SUCCESS);
     74: }

     76: static PetscErrorCode SNESComputeFunction_DMLocal(SNES snes, Vec X, Vec F, PetscCtx ctx)
     77: {
     78:   DMSNES_Local *dmlocalsnes = (DMSNES_Local *)ctx;
     79:   DM            dm;
     80:   Vec           Xloc, Floc;
     81:   PetscBool     transform;

     83:   PetscFunctionBegin;
     87:   PetscCall(SNESGetDM(snes, &dm));
     88:   PetscCall(DMGetLocalVector(dm, &Xloc));
     89:   PetscCall(DMGetLocalVector(dm, &Floc));
     90:   PetscCall(VecZeroEntries(Xloc));
     91:   PetscCall(VecZeroEntries(Floc));
     92:   /* Non-conforming routines needs boundary values before G2L */
     93:   if (dmlocalsnes->boundarylocal) PetscCall((*dmlocalsnes->boundarylocal)(dm, Xloc, dmlocalsnes->boundarylocalctx));
     94:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, Xloc));
     95:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, Xloc));
     96:   /* Need to reset boundary values if we transformed */
     97:   PetscCall(DMHasBasisTransform(dm, &transform));
     98:   if (transform && dmlocalsnes->boundarylocal) PetscCall((*dmlocalsnes->boundarylocal)(dm, Xloc, dmlocalsnes->boundarylocalctx));
     99:   CHKMEMQ;
    100:   PetscCall((*dmlocalsnes->residuallocal)(dm, Xloc, Floc, dmlocalsnes->residuallocalctx));
    101:   CHKMEMQ;
    102:   PetscCall(VecZeroEntries(F));
    103:   PetscCall(DMLocalToGlobalBegin(dm, Floc, ADD_VALUES, F));
    104:   PetscCall(DMLocalToGlobalEnd(dm, Floc, ADD_VALUES, F));
    105:   PetscCall(DMRestoreLocalVector(dm, &Floc));
    106:   PetscCall(DMRestoreLocalVector(dm, &Xloc));
    107:   {
    108:     char        name[PETSC_MAX_PATH_LEN];
    109:     char        oldname[PETSC_MAX_PATH_LEN];
    110:     const char *tmp;
    111:     PetscInt    it;

    113:     PetscCall(SNESGetIterationNumber(snes, &it));
    114:     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN, "Solution, Iterate %" PetscInt_FMT, it));
    115:     PetscCall(PetscObjectGetName((PetscObject)X, &tmp));
    116:     PetscCall(PetscStrncpy(oldname, tmp, PETSC_MAX_PATH_LEN - 1));
    117:     PetscCall(PetscObjectSetName((PetscObject)X, name));
    118:     PetscCall(VecViewFromOptions(X, (PetscObject)snes, "-dmsnes_solution_vec_view"));
    119:     PetscCall(PetscObjectSetName((PetscObject)X, oldname));
    120:     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN, "Residual, Iterate %" PetscInt_FMT, it));
    121:     PetscCall(PetscObjectSetName((PetscObject)F, name));
    122:     PetscCall(VecViewFromOptions(F, (PetscObject)snes, "-dmsnes_residual_vec_view"));
    123:   }
    124:   PetscFunctionReturn(PETSC_SUCCESS);
    125: }

    127: static PetscErrorCode SNESComputeJacobian_DMLocal(SNES snes, Vec X, Mat A, Mat B, PetscCtx ctx)
    128: {
    129:   DMSNES_Local *dmlocalsnes = (DMSNES_Local *)ctx;
    130:   DM            dm;
    131:   Vec           Xloc;
    132:   PetscBool     transform;

    134:   PetscFunctionBegin;
    135:   PetscCall(SNESGetDM(snes, &dm));
    136:   if (dmlocalsnes->jacobianlocal) {
    137:     PetscCall(DMGetLocalVector(dm, &Xloc));
    138:     PetscCall(VecZeroEntries(Xloc));
    139:     /* Non-conforming routines needs boundary values before G2L */
    140:     if (dmlocalsnes->boundarylocal) PetscCall((*dmlocalsnes->boundarylocal)(dm, Xloc, dmlocalsnes->boundarylocalctx));
    141:     PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, Xloc));
    142:     PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, Xloc));
    143:     /* Need to reset boundary values if we transformed */
    144:     PetscCall(DMHasBasisTransform(dm, &transform));
    145:     if (transform && dmlocalsnes->boundarylocal) PetscCall((*dmlocalsnes->boundarylocal)(dm, Xloc, dmlocalsnes->boundarylocalctx));
    146:     CHKMEMQ;
    147:     PetscCall((*dmlocalsnes->jacobianlocal)(dm, Xloc, A, B, dmlocalsnes->jacobianlocalctx));
    148:     CHKMEMQ;
    149:     PetscCall(DMRestoreLocalVector(dm, &Xloc));
    150:   } else {
    151:     MatFDColoring fdcoloring;
    152:     PetscCall(PetscObjectQuery((PetscObject)dm, "DMDASNES_FDCOLORING", (PetscObject *)&fdcoloring));
    153:     if (!fdcoloring) {
    154:       ISColoring coloring;

    156:       PetscCall(DMCreateColoring(dm, dm->coloringtype, &coloring));
    157:       PetscCall(MatFDColoringCreate(B, coloring, &fdcoloring));
    158:       PetscCall(ISColoringDestroy(&coloring));
    159:       switch (dm->coloringtype) {
    160:       case IS_COLORING_GLOBAL:
    161:         PetscCall(MatFDColoringSetFunction(fdcoloring, (MatFDColoringFn *)SNESComputeFunction_DMLocal, dmlocalsnes));
    162:         break;
    163:       default:
    164:         SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "No support for coloring type '%s'", ISColoringTypes[dm->coloringtype]);
    165:       }
    166:       PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fdcoloring, ((PetscObject)dm)->prefix));
    167:       PetscCall(MatFDColoringSetFromOptions(fdcoloring));
    168:       PetscCall(MatFDColoringSetUp(B, coloring, fdcoloring));
    169:       PetscCall(PetscObjectCompose((PetscObject)dm, "DMDASNES_FDCOLORING", (PetscObject)fdcoloring));
    170:       PetscCall(PetscObjectDereference((PetscObject)fdcoloring));

    172:       /* The following breaks an ugly reference counting loop that deserves a paragraph. MatFDColoringApply() will call
    173:        * VecDuplicate() with the state Vec and store inside the MatFDColoring. This Vec will duplicate the Vec, but the
    174:        * MatFDColoring is composed with the DM. We dereference the DM here so that the reference count will eventually
    175:        * drop to 0. Note the code in DMDestroy() that exits early for a negative reference count. That code path will be
    176:        * taken when the PetscObjectList for the Vec inside MatFDColoring is destroyed.
    177:        */
    178:       PetscCall(PetscObjectDereference((PetscObject)dm));
    179:     }
    180:     PetscCall(MatFDColoringApply(B, fdcoloring, X, snes));
    181:   }
    182:   /* This will be redundant if the user called both, but it's too common to forget. */
    183:   if (A != B) {
    184:     PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
    185:     PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
    186:   }
    187:   PetscFunctionReturn(PETSC_SUCCESS);
    188: }

    190: /*@C
    191:   DMSNESSetObjectiveLocal - set a local objective evaluation function. This function is called with local vector
    192:   containing the local vector information PLUS ghost point information. It should compute a result for all local
    193:   elements and `DMSNES` will automatically accumulate the overlapping values.

    195:   Logically Collective

    197:   Input Parameters:
    198: + dm   - `DM` to associate callback with
    199: . func - local objective evaluation
    200: - ctx  - optional context for local objective function evaluation

    202:   Calling sequence of func:
    203: + dm  - the `DM`
    204: . x   - the location where the objective is to be evaluated
    205: . obj - the value of the objective function
    206: - ctx - optional context for the local objective function evaluation

    208:   Level: advanced

    210: .seealso: `DMSNESSetFunctionLocal()`, `DMSNESSetJacobianLocal()`
    211: @*/
    212: PetscErrorCode DMSNESSetObjectiveLocal(DM dm, PetscErrorCode (*func)(DM dm, Vec x, PetscReal *obj, PetscCtx ctx), PetscCtx ctx)
    213: {
    214:   DMSNES        sdm;
    215:   DMSNES_Local *dmlocalsnes;

    217:   PetscFunctionBegin;
    219:   PetscCall(DMGetDMSNESWrite(dm, &sdm));
    220:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));

    222:   dmlocalsnes->objectivelocal    = func;
    223:   dmlocalsnes->objectivelocalctx = ctx;

    225:   PetscCall(DMSNESSetObjective(dm, SNESComputeObjective_DMLocal, dmlocalsnes));
    226:   PetscFunctionReturn(PETSC_SUCCESS);
    227: }

    229: /*@C
    230:   DMSNESSetFunctionLocal - set a local residual evaluation function. This function is called with local vector
    231:   containing the local vector information PLUS ghost point information. It should compute a result for all local
    232:   elements and `DMSNES` will automatically accumulate the overlapping values.

    234:   Logically Collective

    236:   Input Parameters:
    237: + dm   - `DM` to associate callback with
    238: . func - local residual evaluation
    239: - ctx  - optional context for local residual evaluation

    241:   Calling sequence of `func`:
    242: + dm  - `DM` for the function
    243: . x   - vector to state at which to evaluate residual
    244: . f   - vector to hold the function evaluation
    245: - ctx - optional context passed above

    247:   Level: advanced

    249: .seealso: [](ch_snes), `DMSNESSetFunction()`, `DMSNESSetJacobianLocal()`
    250: @*/
    251: PetscErrorCode DMSNESSetFunctionLocal(DM dm, PetscErrorCode (*func)(DM dm, Vec x, Vec f, PetscCtx ctx), PetscCtx ctx) PeNSS
    252: {
    253:   DMSNES        sdm;
    254:   DMSNES_Local *dmlocalsnes;

    256:   PetscFunctionBegin;
    258:   PetscCall(DMGetDMSNESWrite(dm, &sdm));
    259:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));

    261:   dmlocalsnes->residuallocal    = func;
    262:   dmlocalsnes->residuallocalctx = ctx;

    264:   PetscCall(DMSNESSetFunction(dm, SNESComputeFunction_DMLocal, dmlocalsnes));
    265:   if (!sdm->ops->computejacobian) { /* Call us for the Jacobian too, can be overridden by the user. */
    266:     PetscCall(DMSNESSetJacobian(dm, SNESComputeJacobian_DMLocal, dmlocalsnes));
    267:   }
    268:   PetscFunctionReturn(PETSC_SUCCESS);
    269: }

    271: /*@C
    272:   DMSNESSetBoundaryLocal - set a function to insert, for example, essential boundary conditions into a ghosted solution vector

    274:   Logically Collective

    276:   Input Parameters:
    277: + dm   - `DM` to associate callback with
    278: . func - local boundary value evaluation
    279: - ctx  - optional context for local boundary value evaluation

    281:   Calling sequence of `func`:
    282: + dm  - the `DM` context
    283: . X   - ghosted solution vector, appropriate locations (such as essential boundary condition nodes) should be filled
    284: - ctx - option context passed in `DMSNESSetBoundaryLocal()`

    286:   Level: advanced

    288: .seealso: [](ch_snes), `DMSNESSetObjectiveLocal()`, `DMSNESSetFunctionLocal()`, `DMSNESSetJacobianLocal()`
    289: @*/
    290: PetscErrorCode DMSNESSetBoundaryLocal(DM dm, PetscErrorCode (*func)(DM dm, Vec X, PetscCtx ctx), PetscCtx ctx)
    291: {
    292:   DMSNES        sdm;
    293:   DMSNES_Local *dmlocalsnes;

    295:   PetscFunctionBegin;
    297:   PetscCall(DMGetDMSNESWrite(dm, &sdm));
    298:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));

    300:   dmlocalsnes->boundarylocal    = func;
    301:   dmlocalsnes->boundarylocalctx = ctx;
    302:   PetscFunctionReturn(PETSC_SUCCESS);
    303: }

    305: /*@C
    306:   DMSNESSetJacobianLocal - set a local Jacobian evaluation function

    308:   Logically Collective

    310:   Input Parameters:
    311: + dm   - `DM` to associate callback with
    312: . func - local Jacobian evaluation
    313: - ctx  - optional context for local Jacobian evaluation

    315:   Calling sequence of `func`:
    316: + dm  - the `DM` context
    317: . X   - current solution vector (ghosted or not?)
    318: . J   - the Jacobian
    319: . Jp  - approximate Jacobian used to compute the preconditioner, often `J`
    320: - ctx - a user provided context

    322:   Level: advanced

    324: .seealso: [](ch_snes), `DMSNESSetObjectiveLocal()`, `DMSNESSetFunctionLocal()`, `DMSNESSetBoundaryLocal()`
    325: @*/
    326: PetscErrorCode DMSNESSetJacobianLocal(DM dm, PetscErrorCode (*func)(DM dm, Vec X, Mat J, Mat Jp, PetscCtx ctx), PetscCtx ctx) PeNSS
    327: {
    328:   DMSNES        sdm;
    329:   DMSNES_Local *dmlocalsnes;

    331:   PetscFunctionBegin;
    333:   PetscCall(DMGetDMSNESWrite(dm, &sdm));
    334:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));

    336:   dmlocalsnes->jacobianlocal    = func;
    337:   dmlocalsnes->jacobianlocalctx = ctx;

    339:   PetscCall(DMSNESSetJacobian(dm, SNESComputeJacobian_DMLocal, dmlocalsnes));
    340:   PetscFunctionReturn(PETSC_SUCCESS);
    341: }

    343: /*@C
    344:   DMSNESGetObjectiveLocal - get the local objective evaluation function information set with `DMSNESSetObjectiveLocal()`.

    346:   Not Collective

    348:   Input Parameter:
    349: . dm - `DM` with the associated callback

    351:   Output Parameters:
    352: + func - local objective evaluation
    353: - ctx  - context for local residual evaluation

    355:   Calling sequence of func:
    356: + dm  - the `DM`
    357: . x   - the location where the objective function is to be evaluated
    358: . obj - the value of the objective function
    359: - ctx - optional context for the local objective function evaluation

    361:   Level: beginner

    363: .seealso: `DMSNESSetObjective()`, `DMSNESSetObjectiveLocal()`, `DMSNESSetFunctionLocal()`
    364: @*/
    365: PetscErrorCode DMSNESGetObjectiveLocal(DM dm, PetscErrorCode (**func)(DM dm, Vec x, PetscReal *obj, PetscCtx ctx), PetscCtxRt ctx) PeNSS
    366: {
    367:   DMSNES        sdm;
    368:   DMSNES_Local *dmlocalsnes;

    370:   PetscFunctionBegin;
    372:   PetscCall(DMGetDMSNES(dm, &sdm));
    373:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));
    374:   if (func) *func = dmlocalsnes->objectivelocal;
    375:   if (ctx) *(void **)ctx = dmlocalsnes->objectivelocalctx;
    376:   PetscFunctionReturn(PETSC_SUCCESS);
    377: }

    379: /*@C
    380:   DMSNESGetFunctionLocal - get the local residual evaluation function information set with `DMSNESSetFunctionLocal()`.

    382:   Not Collective

    384:   Input Parameter:
    385: . dm - `DM` with the associated callback

    387:   Output Parameters:
    388: + func - local residual evaluation
    389: - ctx  - context for local residual evaluation

    391:   Calling sequence of `func`:
    392: + dm  - `DM` for the function
    393: . x   - vector to state at which to evaluate residual
    394: . f   - vector to hold the function evaluation
    395: - ctx - optional context passed above

    397:   Level: beginner

    399: .seealso: [](ch_snes), `DMSNESSetFunction()`, `DMSNESSetFunctionLocal()`, `DMSNESSetJacobianLocal()`
    400: @*/
    401: PetscErrorCode DMSNESGetFunctionLocal(DM dm, PetscErrorCode (**func)(DM dm, Vec x, Vec f, PetscCtx ctx), PetscCtxRt ctx)
    402: {
    403:   DMSNES        sdm;
    404:   DMSNES_Local *dmlocalsnes;

    406:   PetscFunctionBegin;
    408:   PetscCall(DMGetDMSNES(dm, &sdm));
    409:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));
    410:   if (func) *func = dmlocalsnes->residuallocal;
    411:   if (ctx) *(void **)ctx = dmlocalsnes->residuallocalctx;
    412:   PetscFunctionReturn(PETSC_SUCCESS);
    413: }

    415: /*@C
    416:   DMSNESGetBoundaryLocal - get the local boundary value function set with `DMSNESSetBoundaryLocal()`.

    418:   Not Collective

    420:   Input Parameter:
    421: . dm - `DM` with the associated callback

    423:   Output Parameters:
    424: + func - local boundary value evaluation
    425: - ctx  - context for local boundary value evaluation

    427:   Calling sequence of `func`:
    428: + dm  - the `DM` context
    429: . X   - ghosted solution vector, appropriate locations (such as essential boundary condition nodes) should be filled
    430: - ctx - option context passed in `DMSNESSetBoundaryLocal()`

    432:   Level: intermediate

    434: .seealso: [](ch_snes), `DMSNESSetFunctionLocal()`, `DMSNESSetBoundaryLocal()`, `DMSNESSetJacobianLocal()`
    435: @*/
    436: PetscErrorCode DMSNESGetBoundaryLocal(DM dm, PetscErrorCode (**func)(DM dm, Vec X, PetscCtx ctx), PetscCtxRt ctx)
    437: {
    438:   DMSNES        sdm;
    439:   DMSNES_Local *dmlocalsnes;

    441:   PetscFunctionBegin;
    443:   PetscCall(DMGetDMSNES(dm, &sdm));
    444:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));
    445:   if (func) *func = dmlocalsnes->boundarylocal;
    446:   if (ctx) *(void **)ctx = dmlocalsnes->boundarylocalctx;
    447:   PetscFunctionReturn(PETSC_SUCCESS);
    448: }

    450: /*@C
    451:   DMSNESGetJacobianLocal - the local Jacobian evaluation function set with `DMSNESSetJacobianLocal()`.

    453:   Logically Collective

    455:   Input Parameter:
    456: . dm - `DM` with the associated callback

    458:   Output Parameters:
    459: + func - local Jacobian evaluation
    460: - ctx  - context for local Jacobian evaluation

    462:   Calling sequence of `func`:
    463: + dm  - the `DM` context
    464: . X   - current solution vector (ghosted or not?)
    465: . J   - the Jacobian
    466: . Jp  - approximate Jacobian used to compute the preconditioner, often `J`
    467: - ctx - a user provided context

    469:   Level: beginner

    471: .seealso: [](ch_snes), `DMSNESSetJacobianLocal()`, `DMSNESSetJacobian()`
    472: @*/
    473: PetscErrorCode DMSNESGetJacobianLocal(DM dm, PetscErrorCode (**func)(DM dm, Vec X, Mat J, Mat Jp, PetscCtx ctx), PetscCtxRt ctx)
    474: {
    475:   DMSNES        sdm;
    476:   DMSNES_Local *dmlocalsnes;

    478:   PetscFunctionBegin;
    480:   PetscCall(DMGetDMSNES(dm, &sdm));
    481:   PetscCall(DMLocalSNESGetContext(dm, sdm, &dmlocalsnes));
    482:   if (func) *func = dmlocalsnes->jacobianlocal;
    483:   if (ctx) *(void **)ctx = dmlocalsnes->jacobianlocalctx;
    484:   PetscFunctionReturn(PETSC_SUCCESS);
    485: }