Date: 2025-03-29T05:58:40+00:00

Actual source code: snesmfj.c

      1: #include <petsc/private/snesimpl.h>
      2: #include <petscdm.h>
      3: #include <../src/mat/impls/mffd/mffdimpl.h>
      4: #include <petsc/private/matimpl.h>

      6: /*@
      7:   MatMFFDComputeJacobian - Tells the matrix-free Jacobian object the new location at which
      8:   Jacobian matrix-vector products will be computed at, i.e. J(x) * a. The x is obtained
      9:   from the `SNES` object (using `SNESGetSolution()`).

     11:   Collective

     13:   Input Parameters:
     14: + snes  - the nonlinear solver context
     15: . x     - the point at which the Jacobian-vector products will be performed
     16: . jac   - the matrix-free Jacobian object of `MatType` `MATMFFD`, likely obtained with `MatCreateSNESMF()`
     17: . B     - either the same as `jac` or another matrix type (ignored)
     18: - dummy - the user context (ignored)

     20:   Options Database Key:
     21: . -snes_mf - use the matrix created with `MatSNESMFCreate()` to setup the Jacobian for each new solution in the Newton process

     23:   Level: developer

     25:   Notes:
     26:   If `MatMFFDSetBase()` is ever called on `jac` then this routine will NO longer get
     27:   the `x` from the `SNES` object and `MatMFFDSetBase()` must from that point on be used to
     28:   change the base vector `x`.

     30:   This can be passed into `SNESSetJacobian()` as the Jacobian evaluation function argument
     31:   when using a completely matrix-free solver,
     32:   that is the B matrix is also the same matrix operator. This is used when you select
     33:   -snes_mf but rarely used directly by users. (All this routine does is call `MatAssemblyBegin/End()` on
     34:   the `Mat` `jac`.)

     36: .seealso: [](ch_snes), `MatMFFDGetH()`, `MatCreateSNESMF()`, `MatMFFDSetBase()`, `MatCreateMFFD()`, `MATMFFD`,
     37:           `MatMFFDSetHHistory()`, `MatMFFDSetFunctionError()`, `SNESSetJacobian()`
     38: @*/
     39: PetscErrorCode MatMFFDComputeJacobian(SNES snes, Vec x, Mat jac, Mat B, void *dummy)
     40: {
     41:   PetscFunctionBegin;
     42:   PetscCall(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
     43:   PetscCall(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
     44:   PetscFunctionReturn(PETSC_SUCCESS);
     45: }

     47: PETSC_EXTERN PetscErrorCode MatAssemblyEnd_MFFD(Mat, MatAssemblyType);
     48: PETSC_EXTERN PetscErrorCode MatMFFDSetBase_MFFD(Mat, Vec, Vec);

     50: /*@
     51:   MatSNESMFGetSNES - returns the `SNES` associated with a matrix created with `MatCreateSNESMF()`

     53:   Not Collective

     55:   Input Parameter:
     56: . J - the matrix

     58:   Output Parameter:
     59: . snes - the `SNES` object

     61:   Level: advanced

     63: .seealso: [](ch_snes), `Mat`, `SNES`, `MatCreateSNESMF()`
     64: @*/
     65: PetscErrorCode MatSNESMFGetSNES(Mat J, SNES *snes)
     66: {
     67:   MatMFFD j;

     69:   PetscFunctionBegin;
     70:   PetscCall(MatShellGetContext(J, &j));
     71:   *snes = (SNES)j->ctx;
     72:   PetscFunctionReturn(PETSC_SUCCESS);
     73: }

     75: /*
     76:    MatAssemblyEnd_SNESMF - Calls MatAssemblyEnd_MFFD() and then sets the
     77:     base from the SNES context

     79: */
     80: static PetscErrorCode MatAssemblyEnd_SNESMF(Mat J, MatAssemblyType mt)
     81: {
     82:   MatMFFD j;
     83:   SNES    snes;
     84:   Vec     u, f;
     85:   DM      dm;
     86:   DMSNES  dms;

     88:   PetscFunctionBegin;
     89:   PetscCall(MatShellGetContext(J, &j));
     90:   snes = (SNES)j->ctx;
     91:   PetscCall(MatAssemblyEnd_MFFD(J, mt));

     93:   PetscCall(SNESGetSolution(snes, &u));
     94:   PetscCall(SNESGetDM(snes, &dm));
     95:   PetscCall(DMGetDMSNES(dm, &dms));
     96:   if ((j->func == (PetscErrorCode (*)(void *, Vec, Vec))SNESComputeFunction) && !dms->ops->computemffunction) {
     97:     PetscCall(SNESGetFunction(snes, &f, NULL, NULL));
     98:     PetscCall(MatMFFDSetBase_MFFD(J, u, f));
     99:   } else {
    100:     /* f value known by SNES is not correct for other differencing function */
    101:     PetscCall(MatMFFDSetBase_MFFD(J, u, NULL));
    102:   }
    103:   PetscFunctionReturn(PETSC_SUCCESS);
    104: }

    106: /*
    107:    MatAssemblyEnd_SNESMF_UseBase - Calls MatAssemblyEnd_MFFD() and then sets the
    108:     base from the SNES context. This version will cause the base to be used for differencing
    109:     even if the func is not SNESComputeFunction. See: MatSNESMFUseBase()

    111: */
    112: static PetscErrorCode MatAssemblyEnd_SNESMF_UseBase(Mat J, MatAssemblyType mt)
    113: {
    114:   MatMFFD j;
    115:   SNES    snes;
    116:   Vec     u, f;

    118:   PetscFunctionBegin;
    119:   PetscCall(MatAssemblyEnd_MFFD(J, mt));
    120:   PetscCall(MatShellGetContext(J, &j));
    121:   snes = (SNES)j->ctx;
    122:   PetscCall(SNESGetSolution(snes, &u));
    123:   PetscCall(SNESGetFunction(snes, &f, NULL, NULL));
    124:   PetscCall(MatMFFDSetBase_MFFD(J, u, f));
    125:   PetscFunctionReturn(PETSC_SUCCESS);
    126: }

    128: /*
    129:     This routine resets the MatAssemblyEnd() for the MatMFFD created from MatCreateSNESMF() so that it NO longer
    130:   uses the solution in the SNES object to update the base. See the warning in MatCreateSNESMF().
    131: */
    132: static PetscErrorCode MatMFFDSetBase_SNESMF(Mat J, Vec U, Vec F)
    133: {
    134:   PetscFunctionBegin;
    135:   PetscCall(MatMFFDSetBase_MFFD(J, U, F));
    136:   J->ops->assemblyend = MatAssemblyEnd_MFFD;
    137:   PetscFunctionReturn(PETSC_SUCCESS);
    138: }

    140: static PetscErrorCode MatSNESMFSetReuseBase_SNESMF(Mat J, PetscBool use)
    141: {
    142:   PetscFunctionBegin;
    143:   if (use) {
    144:     J->ops->assemblyend = MatAssemblyEnd_SNESMF_UseBase;
    145:   } else {
    146:     J->ops->assemblyend = MatAssemblyEnd_SNESMF;
    147:   }
    148:   PetscFunctionReturn(PETSC_SUCCESS);
    149: }

    151: /*@
    152:   MatSNESMFSetReuseBase - Causes the base vector to be used for differencing even if the function provided to `SNESSetFunction()` is not the
    153:   same as that provided to `MatMFFDSetFunction()`.

    155:   Logically Collective

    157:   Input Parameters:
    158: + J   - the `MATMFFD` matrix
    159: - use - if true always reuse the base vector instead of recomputing f(u) even if the function in the `MATMFFD` is
    160:           not `SNESComputeFunction()`

    162:   Level: advanced

    164:   Note:
    165:   Care must be taken when using this routine to insure that the function provided to `MatMFFDSetFunction()`, call it F_MF() is compatible with
    166:   with that provided to `SNESSetFunction()`, call it F_SNES(). That is, (F_MF(u + h*d) - F_SNES(u))/h has to approximate the derivative

    168:   Developer Notes:
    169:   This was provided for the MOOSE team who desired to have a `SNESSetFunction()` function that could change configurations (similar to variable
    170:   switching) to contacts while the function provided to `MatMFFDSetFunction()` cannot. Except for the possibility of changing the configuration
    171:   both functions compute the same mathematical function so the differencing makes sense.

    173: .seealso: [](ch_snes), `SNES`, `MATMFFD`, `MatMFFDSetFunction()`, `SNESSetFunction()`, `MatCreateSNESMF()`, `MatSNESMFGetReuseBase()`
    174: @*/
    175: PetscErrorCode MatSNESMFSetReuseBase(Mat J, PetscBool use)
    176: {
    177:   PetscFunctionBegin;
    179:   PetscTryMethod(J, "MatSNESMFSetReuseBase_C", (Mat, PetscBool), (J, use));
    180:   PetscFunctionReturn(PETSC_SUCCESS);
    181: }

    183: static PetscErrorCode MatSNESMFGetReuseBase_SNESMF(Mat J, PetscBool *use)
    184: {
    185:   PetscFunctionBegin;
    186:   if (J->ops->assemblyend == MatAssemblyEnd_SNESMF_UseBase) *use = PETSC_TRUE;
    187:   else *use = PETSC_FALSE;
    188:   PetscFunctionReturn(PETSC_SUCCESS);
    189: }

    191: /*@
    192:   MatSNESMFGetReuseBase - Determines if the base vector is to be used for differencing even if the function provided to `SNESSetFunction()` is not the
    193:   same as that provided to `MatMFFDSetFunction()`.

    195:   Logically Collective

    197:   Input Parameter:
    198: . J - the `MATMFFD` matrix

    200:   Output Parameter:
    201: . use - if true always reuse the base vector instead of recomputing f(u) even if the function in the `MATMFFD` is
    202:           not `SNESComputeFunction()`

    204:   Level: advanced

    206:   Note:
    207:   See `MatSNESMFSetReuseBase()`

    209: .seealso: [](ch_snes), `Mat`, `SNES`, `MatSNESMFSetReuseBase()`, `MatCreateSNESMF()`
    210: @*/
    211: PetscErrorCode MatSNESMFGetReuseBase(Mat J, PetscBool *use)
    212: {
    213:   PetscFunctionBegin;
    215:   PetscUseMethod(J, "MatSNESMFGetReuseBase_C", (Mat, PetscBool *), (J, use));
    216:   PetscFunctionReturn(PETSC_SUCCESS);
    217: }

    219: /*@
    220:   MatCreateSNESMF - Creates a finite differencing based matrix-free matrix context for use with
    221:   a `SNES` solver.  This matrix can be used as the Jacobian argument for
    222:   the routine `SNESSetJacobian()`. See `MatCreateMFFD()` for details on how
    223:   the finite difference computation is done.

    225:   Collective

    227:   Input Parameters:
    228: . snes - the `SNES` context

    230:   Output Parameter:
    231: . J - the matrix-free matrix which is of type `MATMFFD`

    233:   Level: advanced

    235:   Notes:
    236:   You can call `SNESSetJacobian()` with `MatMFFDComputeJacobian()` if you are not using a different
    237:   preconditioner matrix

    239:   If you wish to provide a different function to do differencing on to compute the matrix-free operator than
    240:   that provided to `SNESSetFunction()` then call `MatMFFDSetFunction()` with your function after this call.

    242:   The difference between this routine and `MatCreateMFFD()` is that this matrix
    243:   automatically gets the current base vector from the `SNES` object and not from an
    244:   explicit call to `MatMFFDSetBase()`.

    246:   If `MatMFFDSetBase()` is ever called on `jac` then this routine will NO longer get
    247:   the x from the `SNES` object and `MatMFFDSetBase()` must from that point on be used to
    248:   change the base vector `x`.

    250:   Using a different function for the differencing will not work if you are using non-linear left preconditioning.

    252:   This uses finite-differencing to apply the operator. To create a matrix-free `Mat` whose matrix-vector operator you
    253:   provide with your own function use `MatCreateShell()`.

    255:   Developer Note:
    256:   This function should really be called `MatCreateSNESMFFD()` in correspondence to `MatCreateMFFD()` to clearly indicate
    257:   that this is for using finite differences to apply the operator matrix-free.

    259: .seealso: [](ch_snes), `SNES`, `MATMFFD`, `MatDestroy()`, `MatMFFDSetFunction()`, `MatMFFDSetFunctionError()`, `MatMFFDDSSetUmin()`
    260:           `MatMFFDSetHHistory()`, `MatMFFDResetHHistory()`, `MatCreateMFFD()`, `MatCreateShell()`,
    261:           `MatMFFDGetH()`, `MatMFFDRegister()`, `MatMFFDComputeJacobian()`, `MatSNESMFSetReuseBase()`, `MatSNESMFGetReuseBase()`
    262: @*/
    263: PetscErrorCode MatCreateSNESMF(SNES snes, Mat *J)
    264: {
    265:   PetscInt n, N;
    266:   MatMFFD  mf;

    268:   PetscFunctionBegin;
    269:   if (snes->vec_func) {
    270:     PetscCall(VecGetLocalSize(snes->vec_func, &n));
    271:     PetscCall(VecGetSize(snes->vec_func, &N));
    272:   } else if (snes->dm) {
    273:     Vec tmp;
    274:     PetscCall(DMGetGlobalVector(snes->dm, &tmp));
    275:     PetscCall(VecGetLocalSize(tmp, &n));
    276:     PetscCall(VecGetSize(tmp, &N));
    277:     PetscCall(DMRestoreGlobalVector(snes->dm, &tmp));
    278:   } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Must call SNESSetFunction() or SNESSetDM() first");
    279:   PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)snes), n, n, N, N, J));
    280:   PetscCall(MatShellGetContext(*J, &mf));
    281:   mf->ctx = snes;

    283:   if (snes->npc && snes->npcside == PC_LEFT) {
    284:     PetscCall(MatMFFDSetFunction(*J, (PetscErrorCode (*)(void *, Vec, Vec))SNESComputeFunctionDefaultNPC, snes));
    285:   } else {
    286:     DM     dm;
    287:     DMSNES dms;

    289:     PetscCall(SNESGetDM(snes, &dm));
    290:     PetscCall(DMGetDMSNES(dm, &dms));
    291:     PetscCall(MatMFFDSetFunction(*J, (PetscErrorCode (*)(void *, Vec, Vec))(dms->ops->computemffunction ? SNESComputeMFFunction : SNESComputeFunction), snes));
    292:   }
    293:   (*J)->ops->assemblyend = MatAssemblyEnd_SNESMF;

    295:   PetscCall(PetscObjectComposeFunction((PetscObject)*J, "MatMFFDSetBase_C", MatMFFDSetBase_SNESMF));
    296:   PetscCall(PetscObjectComposeFunction((PetscObject)*J, "MatSNESMFSetReuseBase_C", MatSNESMFSetReuseBase_SNESMF));
    297:   PetscCall(PetscObjectComposeFunction((PetscObject)*J, "MatSNESMFGetReuseBase_C", MatSNESMFGetReuseBase_SNESMF));
    298:   PetscFunctionReturn(PETSC_SUCCESS);
    299: }