Actual source code: virs.c

  1: #include <../src/snes/impls/vi/rs/virsimpl.h>
  2: #include <petsc/private/dmimpl.h>
  3: #include <petsc/private/vecimpl.h>

  5: /*@
  6:   SNESVIGetInactiveSet - Gets the global indices for the inactive set variables (these correspond to the degrees of freedom the linear
  7:   system is solved on)

  9:   Input Parameter:
 10: . snes - the `SNES` context

 12:   Output Parameter:
 13: . inact - inactive set index set

 15:   Level: advanced

 17:   Note:
 18:   See `SNESVINEWTONRSLS` for a concise description of the active and inactive sets

 20: .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`
 21: @*/
 22: PetscErrorCode SNESVIGetInactiveSet(SNES snes, IS *inact)
 23: {
 24:   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;

 26:   PetscFunctionBegin;
 27:   *inact = vi->IS_inact;
 28:   PetscFunctionReturn(PETSC_SUCCESS);
 29: }

 31: /*
 32:     Provides a wrapper to a DM to allow it to be used to generated the interpolation/restriction from the DM for the smaller matrices and vectors
 33:   defined by the reduced space method.

 35:     Simple calls the regular DM interpolation and restricts it to operation on the variables not associated with active constraints.
 36: */
 37: typedef struct {
 38:   PetscInt n; /* size of vectors in the reduced DM space */
 39:   IS       inactive;

 41:   PetscErrorCode (*createinterpolation)(DM, DM, Mat *, Vec *); /* DM's original routines */
 42:   PetscErrorCode (*coarsen)(DM, MPI_Comm, DM *);
 43:   PetscErrorCode (*createglobalvector)(DM, Vec *);
 44:   PetscErrorCode (*createinjection)(DM, DM, Mat *);
 45:   PetscErrorCode (*hascreateinjection)(DM, PetscBool *);

 47:   DM dm; /* when destroying this object we need to reset the above function into the base DM */
 48: } DM_SNESVI;

 50: /*
 51:      DMCreateGlobalVector_SNESVI - Creates global vector of the size of the reduced space
 52: */
 53: static PetscErrorCode DMCreateGlobalVector_SNESVI(DM dm, Vec *vec)
 54: {
 55:   PetscContainer isnes;
 56:   DM_SNESVI     *dmsnesvi;

 58:   PetscFunctionBegin;
 59:   PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes));
 60:   PetscCheck(isnes, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Composed SNES is missing");
 61:   PetscCall(PetscContainerGetPointer(isnes, &dmsnesvi));
 62:   PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)dm), dmsnesvi->n, PETSC_DETERMINE, vec));
 63:   PetscCall(VecSetDM(*vec, dm));
 64:   PetscFunctionReturn(PETSC_SUCCESS);
 65: }

 67: /*
 68:      DMCreateInterpolation_SNESVI - Modifieds the interpolation obtained from the DM by removing all rows and columns associated with active constraints.
 69: */
 70: static PetscErrorCode DMCreateInterpolation_SNESVI(DM dm1, DM dm2, Mat *mat, Vec *vec)
 71: {
 72:   PetscContainer isnes;
 73:   DM_SNESVI     *dmsnesvi1, *dmsnesvi2;
 74:   Mat            interp;

 76:   PetscFunctionBegin;
 77:   PetscCall(PetscObjectQuery((PetscObject)dm1, "VI", (PetscObject *)&isnes));
 78:   PetscCheck(isnes, PetscObjectComm((PetscObject)dm1), PETSC_ERR_PLIB, "Composed VI data structure is missing");
 79:   PetscCall(PetscContainerGetPointer(isnes, &dmsnesvi1));
 80:   PetscCall(PetscObjectQuery((PetscObject)dm2, "VI", (PetscObject *)&isnes));
 81:   PetscCheck(isnes, PetscObjectComm((PetscObject)dm2), PETSC_ERR_PLIB, "Composed VI data structure is missing");
 82:   PetscCall(PetscContainerGetPointer(isnes, &dmsnesvi2));

 84:   PetscCall((*dmsnesvi1->createinterpolation)(dm1, dm2, &interp, NULL));
 85:   PetscCall(MatCreateSubMatrix(interp, dmsnesvi2->inactive, dmsnesvi1->inactive, MAT_INITIAL_MATRIX, mat));
 86:   PetscCall(MatDestroy(&interp));
 87:   *vec = NULL;
 88:   PetscFunctionReturn(PETSC_SUCCESS);
 89: }

 91: /*
 92:      DMCoarsen_SNESVI - Computes the regular coarsened DM then computes additional information about its inactive set
 93: */
 94: static PetscErrorCode DMCoarsen_SNESVI(DM dm1, MPI_Comm comm, DM *dm2)
 95: {
 96:   PetscContainer  isnes;
 97:   DM_SNESVI      *dmsnesvi1;
 98:   Vec             finemarked, coarsemarked;
 99:   IS              inactive;
100:   Mat             inject;
101:   const PetscInt *index;
102:   PetscInt        n, k, cnt = 0, rstart, *coarseindex;
103:   PetscScalar    *marked;

105:   PetscFunctionBegin;
106:   PetscCall(PetscObjectQuery((PetscObject)dm1, "VI", (PetscObject *)&isnes));
107:   PetscCheck(isnes, PetscObjectComm((PetscObject)dm1), PETSC_ERR_PLIB, "Composed VI data structure is missing");
108:   PetscCall(PetscContainerGetPointer(isnes, &dmsnesvi1));

110:   /* get the original coarsen */
111:   PetscCall((*dmsnesvi1->coarsen)(dm1, comm, dm2));

113:   /* not sure why this extra reference is needed, but without the dm2 disappears too early */
114:   /* Updating the KSPCreateVecs() to avoid using DMGetGlobalVector() when matrix is available removes the need for this reference? */
115:   /*  PetscCall(PetscObjectReference((PetscObject)*dm2));*/

117:   /* need to set back global vectors in order to use the original injection */
118:   PetscCall(DMClearGlobalVectors(dm1));

120:   dm1->ops->createglobalvector = dmsnesvi1->createglobalvector;

122:   PetscCall(DMCreateGlobalVector(dm1, &finemarked));
123:   PetscCall(DMCreateGlobalVector(*dm2, &coarsemarked));

125:   /*
126:      fill finemarked with locations of inactive points
127:   */
128:   PetscCall(ISGetIndices(dmsnesvi1->inactive, &index));
129:   PetscCall(ISGetLocalSize(dmsnesvi1->inactive, &n));
130:   for (k = 0; k < n; k++) PetscCall(VecSetValue(finemarked, index[k], 1.0, INSERT_VALUES));
131:   PetscCall(VecAssemblyBegin(finemarked));
132:   PetscCall(VecAssemblyEnd(finemarked));

134:   PetscCall(DMCreateInjection(*dm2, dm1, &inject));
135:   PetscCall(MatRestrict(inject, finemarked, coarsemarked));
136:   PetscCall(MatDestroy(&inject));

138:   /*
139:      create index set list of coarse inactive points from coarsemarked
140:   */
141:   PetscCall(VecGetLocalSize(coarsemarked, &n));
142:   PetscCall(VecGetOwnershipRange(coarsemarked, &rstart, NULL));
143:   PetscCall(VecGetArray(coarsemarked, &marked));
144:   for (k = 0; k < n; k++) {
145:     if (marked[k] != 0.0) cnt++;
146:   }
147:   PetscCall(PetscMalloc1(cnt, &coarseindex));
148:   cnt = 0;
149:   for (k = 0; k < n; k++) {
150:     if (marked[k] != 0.0) coarseindex[cnt++] = k + rstart;
151:   }
152:   PetscCall(VecRestoreArray(coarsemarked, &marked));
153:   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)coarsemarked), cnt, coarseindex, PETSC_OWN_POINTER, &inactive));

155:   PetscCall(DMClearGlobalVectors(dm1));

157:   dm1->ops->createglobalvector = DMCreateGlobalVector_SNESVI;

159:   PetscCall(DMSetVI(*dm2, inactive));

161:   PetscCall(VecDestroy(&finemarked));
162:   PetscCall(VecDestroy(&coarsemarked));
163:   PetscCall(ISDestroy(&inactive));
164:   PetscFunctionReturn(PETSC_SUCCESS);
165: }

167: static PetscErrorCode DMDestroy_SNESVI(PetscCtxRt ctx)
168: {
169:   DM_SNESVI *dmsnesvi = *(DM_SNESVI **)ctx;

171:   PetscFunctionBegin;
172:   /* reset the base methods in the DM object that were changed when the DM_SNESVI was reset */
173:   dmsnesvi->dm->ops->createinterpolation = dmsnesvi->createinterpolation;
174:   dmsnesvi->dm->ops->coarsen             = dmsnesvi->coarsen;
175:   dmsnesvi->dm->ops->createglobalvector  = dmsnesvi->createglobalvector;
176:   dmsnesvi->dm->ops->createinjection     = dmsnesvi->createinjection;
177:   dmsnesvi->dm->ops->hascreateinjection  = dmsnesvi->hascreateinjection;
178:   /* need to clear out this vectors because some of them may not have a reference to the DM
179:     but they are counted as having references to the DM in DMDestroy() */
180:   PetscCall(DMClearGlobalVectors(dmsnesvi->dm));

182:   PetscCall(ISDestroy(&dmsnesvi->inactive));
183:   PetscCall(PetscFree(dmsnesvi));
184:   PetscFunctionReturn(PETSC_SUCCESS);
185: }

187: /*@
188:   DMSetVI - Marks a `DM` as associated with a VI problem. This causes the interpolation/restriction operators to
189:   be restricted to only those variables NOT associated with active constraints.

191:   Logically Collective

193:   Input Parameters:
194: + dm       - the `DM` object
195: - inactive - an `IS` indicating which points are currently not active

197:   Level: intermediate

199: .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`
200: @*/
201: PetscErrorCode DMSetVI(DM dm, IS inactive)
202: {
203:   PetscContainer isnes;
204:   DM_SNESVI     *dmsnesvi;

206:   PetscFunctionBegin;
207:   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);

209:   PetscCall(PetscObjectReference((PetscObject)inactive));

211:   PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes));
212:   if (!isnes) {
213:     PetscCall(PetscNew(&dmsnesvi));
214:     PetscCall(PetscObjectContainerCompose((PetscObject)dm, "VI", dmsnesvi, DMDestroy_SNESVI));

216:     dmsnesvi->createinterpolation = dm->ops->createinterpolation;
217:     dm->ops->createinterpolation  = DMCreateInterpolation_SNESVI;
218:     dmsnesvi->coarsen             = dm->ops->coarsen;
219:     dm->ops->coarsen              = DMCoarsen_SNESVI;
220:     dmsnesvi->createglobalvector  = dm->ops->createglobalvector;
221:     dm->ops->createglobalvector   = DMCreateGlobalVector_SNESVI;
222:     dmsnesvi->createinjection     = dm->ops->createinjection;
223:     dm->ops->createinjection      = NULL;
224:     dmsnesvi->hascreateinjection  = dm->ops->hascreateinjection;
225:     dm->ops->hascreateinjection   = NULL;
226:   } else {
227:     PetscCall(PetscContainerGetPointer(isnes, &dmsnesvi));
228:     PetscCall(ISDestroy(&dmsnesvi->inactive));
229:   }
230:   PetscCall(DMClearGlobalVectors(dm));
231:   PetscCall(ISGetLocalSize(inactive, &dmsnesvi->n));

233:   dmsnesvi->inactive = inactive;
234:   dmsnesvi->dm       = dm;
235:   PetscFunctionReturn(PETSC_SUCCESS);
236: }

238: /*@
239:   DMDestroyVI - Frees the `DM_SNESVI` object contained in the `DM` and resets any function pointers the reduced-space `SNESVI` code composed onto it

241:   Not Collective

243:   Input Parameter:
244: . dm - the `DM` from which the VI context should be removed (may be `NULL`)

246:   Level: developer

248: .seealso: `DM`, `SNESVINEWTONRSLS`, `SNESVISetVariableBounds()`, `PetscObjectCompose()`
249: @*/
250: PetscErrorCode DMDestroyVI(DM dm)
251: {
252:   PetscFunctionBegin;
253:   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
254:   PetscCall(PetscObjectCompose((PetscObject)dm, "VI", (PetscObject)NULL));
255:   PetscFunctionReturn(PETSC_SUCCESS);
256: }

258: /* Create active and inactive set vectors. The local size of this vector is set and PETSc computes the global size */
259: static PetscErrorCode SNESCreateSubVectors_VINEWTONRSLS(SNES snes, PetscInt n, Vec *newv)
260: {
261:   Vec v;

263:   PetscFunctionBegin;
264:   PetscCall(VecCreate(PetscObjectComm((PetscObject)snes), &v));
265:   PetscCall(VecSetSizes(v, n, PETSC_DECIDE));
266:   PetscCall(VecSetType(v, VECSTANDARD));
267:   *newv = v;
268:   PetscFunctionReturn(PETSC_SUCCESS);
269: }

271: /* Resets the snes PC and KSP when the active set sizes change */
272: static PetscErrorCode SNESVIResetPCandKSP(SNES snes, Mat Amat, Mat Pmat)
273: {
274:   KSP snesksp;

276:   PetscFunctionBegin;
277:   PetscCall(SNESGetKSP(snes, &snesksp));
278:   PetscCall(KSPReset(snesksp));
279:   PetscCall(KSPResetFromOptions(snesksp));

281:   /*
282:   KSP                    kspnew;
283:   PC                     pcnew;
284:   MatSolverType          stype;

286:   PetscCall(KSPCreate(PetscObjectComm((PetscObject)snes),&kspnew));
287:   kspnew->pc_side = snesksp->pc_side;
288:   kspnew->rtol    = snesksp->rtol;
289:   kspnew->abstol    = snesksp->abstol;
290:   kspnew->max_it  = snesksp->max_it;
291:   PetscCall(KSPSetType(kspnew,((PetscObject)snesksp)->type_name));
292:   PetscCall(KSPGetPC(kspnew,&pcnew));
293:   PetscCall(PCSetType(kspnew->pc,((PetscObject)snesksp->pc)->type_name));
294:   PetscCall(PCSetOperators(kspnew->pc,Amat,Pmat));
295:   PetscCall(PCFactorGetMatSolverType(snesksp->pc,&stype));
296:   PetscCall(PCFactorSetMatSolverType(kspnew->pc,stype));
297:   PetscCall(KSPDestroy(&snesksp));
298:   snes->ksp = kspnew;
299:    PetscCall(KSPSetFromOptions(kspnew));*/
300:   PetscFunctionReturn(PETSC_SUCCESS);
301: }

303: /* Variational Inequality solver using reduce space method. No semismooth algorithm is
304:    implemented in this algorithm. It basically identifies the active constraints and does
305:    a linear solve on the other variables (those not associated with the active constraints). */
306: static PetscErrorCode SNESSolve_VINEWTONRSLS(SNES snes)
307: {
308:   SNES_VINEWTONRSLS   *vi = (SNES_VINEWTONRSLS *)snes->data;
309:   PetscInt             maxits, i, lits;
310:   SNESLineSearchReason lsreason;
311:   PetscReal            fnorm, gnorm, xnorm = 0, ynorm;
312:   Vec                  Y, X, F;
313:   KSPConvergedReason   kspreason;
314:   KSP                  ksp;
315:   PC                   pc;

317:   PetscFunctionBegin;
318:   /* Multigrid must use Galerkin for coarse grids with active set/reduced space methods; cannot rediscretize on coarser grids*/
319:   PetscCall(SNESGetKSP(snes, &ksp));
320:   PetscCall(KSPGetPC(ksp, &pc));
321:   PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_BOTH));

323:   snes->numFailures            = 0;
324:   snes->numLinearSolveFailures = 0;
325:   snes->reason                 = SNES_CONVERGED_ITERATING;

327:   maxits = snes->max_its;  /* maximum number of iterations */
328:   X      = snes->vec_sol;  /* solution vector */
329:   F      = snes->vec_func; /* residual vector */
330:   Y      = snes->work[0];  /* work vectors */

332:   PetscCall(SNESLineSearchSetVIFunctions(snes->linesearch, SNESVIProjectOntoBounds, SNESVIComputeInactiveSetFnorm, SNESVIComputeInactiveSetFtY));
333:   PetscCall(SNESLineSearchSetVecs(snes->linesearch, X, NULL, NULL, NULL, NULL));
334:   PetscCall(SNESLineSearchSetUp(snes->linesearch));

336:   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
337:   snes->iter = 0;
338:   snes->norm = 0.0;
339:   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));

341:   PetscCall(SNESVIProjectOntoBounds(snes, X));
342:   PetscCall(SNESComputeFunction(snes, X, F));
343:   PetscCall(SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm));
344:   PetscCall(VecNorm(X, NORM_2, &xnorm)); /* xnorm <- ||x||  */
345:   SNESCheckFunctionDomainError(snes, fnorm);
346:   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
347:   snes->norm = fnorm;
348:   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
349:   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));

351:   /* test convergence */
352:   PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm));
353:   PetscCall(SNESMonitor(snes, 0, fnorm));
354:   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);

356:   for (i = 0; i < maxits; i++) {
357:     IS         IS_act;    /* _act -> active set _inact -> inactive set */
358:     IS         IS_redact; /* redundant active set */
359:     VecScatter scat_act, scat_inact;
360:     PetscInt   nis_act, nis_inact;
361:     Vec        Y_act, Y_inact, F_inact;
362:     Mat        jac_inact_inact, prejac_inact_inact;
363:     PetscBool  isequal;

365:     /* Call general purpose update function */
366:     PetscTryTypeMethod(snes, update, snes->iter);
367:     PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
368:     SNESCheckJacobianDomainError(snes);

370:     /* Create active and inactive index sets */

372:     /*original
373:     PetscCall(SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&vi->IS_inact));
374:      */
375:     PetscCall(SNESVIGetActiveSetIS(snes, X, F, &IS_act));

377:     if (vi->checkredundancy) {
378:       PetscCall((*vi->checkredundancy)(snes, IS_act, &IS_redact, vi->ctxP));
379:       if (IS_redact) {
380:         PetscCall(ISSort(IS_redact));
381:         PetscCall(ISComplement(IS_redact, X->map->rstart, X->map->rend, &vi->IS_inact));
382:         PetscCall(ISDestroy(&IS_redact));
383:       } else {
384:         PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact));
385:       }
386:     } else {
387:       PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact));
388:     }

390:     /* Create inactive set submatrix */
391:     PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact));

393:     if (0) { /* Dead code (temporary developer hack) */
394:       IS keptrows;
395:       PetscCall(MatFindNonzeroRows(jac_inact_inact, &keptrows));
396:       if (keptrows) {
397:         PetscInt        cnt, *nrows, k;
398:         const PetscInt *krows, *inact;
399:         PetscInt        rstart;

401:         PetscCall(MatGetOwnershipRange(jac_inact_inact, &rstart, NULL));
402:         PetscCall(MatDestroy(&jac_inact_inact));
403:         PetscCall(ISDestroy(&IS_act));

405:         PetscCall(ISGetLocalSize(keptrows, &cnt));
406:         PetscCall(ISGetIndices(keptrows, &krows));
407:         PetscCall(ISGetIndices(vi->IS_inact, &inact));
408:         PetscCall(PetscMalloc1(cnt, &nrows));
409:         for (k = 0; k < cnt; k++) nrows[k] = inact[krows[k] - rstart];
410:         PetscCall(ISRestoreIndices(keptrows, &krows));
411:         PetscCall(ISRestoreIndices(vi->IS_inact, &inact));
412:         PetscCall(ISDestroy(&keptrows));
413:         PetscCall(ISDestroy(&vi->IS_inact));

415:         PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), cnt, nrows, PETSC_OWN_POINTER, &vi->IS_inact));
416:         PetscCall(ISComplement(vi->IS_inact, F->map->rstart, F->map->rend, &IS_act));
417:         PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact));
418:       }
419:     }
420:     PetscCall(DMSetVI(snes->dm, vi->IS_inact));
421:     /* remove later */

423:     /*
424:     PetscCall(VecView(vi->xu,PETSC_VIEWER_BINARY_(((PetscObject)vi->xu)->comm)));
425:     PetscCall(VecView(vi->xl,PETSC_VIEWER_BINARY_(((PetscObject)vi->xl)->comm)));
426:     PetscCall(VecView(X,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)X))));
427:     PetscCall(VecView(F,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)F))));
428:     PetscCall(ISView(vi->IS_inact,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)vi->IS_inact))));
429:      */

431:     /* Get sizes of active and inactive sets */
432:     PetscCall(ISGetLocalSize(IS_act, &nis_act));
433:     PetscCall(ISGetLocalSize(vi->IS_inact, &nis_inact));

435:     /* Create active and inactive set vectors */
436:     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &F_inact));
437:     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_act, &Y_act));
438:     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &Y_inact));

440:     /* Create scatter contexts */
441:     PetscCall(VecScatterCreate(Y, IS_act, Y_act, NULL, &scat_act));
442:     PetscCall(VecScatterCreate(Y, vi->IS_inact, Y_inact, NULL, &scat_inact));

444:     /* Do a vec scatter to active and inactive set vectors */
445:     PetscCall(VecScatterBegin(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD));
446:     PetscCall(VecScatterEnd(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD));

448:     PetscCall(VecScatterBegin(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD));
449:     PetscCall(VecScatterEnd(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD));

451:     PetscCall(VecScatterBegin(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD));
452:     PetscCall(VecScatterEnd(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD));

454:     /* Active set direction = 0 */
455:     PetscCall(VecSet(Y_act, 0));
456:     if (snes->jacobian != snes->jacobian_pre) PetscCall(MatCreateSubMatrix(snes->jacobian_pre, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &prejac_inact_inact));
457:     else prejac_inact_inact = jac_inact_inact;

459:     PetscCall(ISEqual(vi->IS_inact_prev, vi->IS_inact, &isequal));
460:     if (!isequal) {
461:       PetscCall(SNESVIResetPCandKSP(snes, jac_inact_inact, prejac_inact_inact));
462:       PetscCall(PCFieldSplitRestrictIS(pc, vi->IS_inact));
463:     }

465:     /*      PetscCall(ISView(vi->IS_inact,0)); */
466:     /*      PetscCall(ISView(IS_act,0));*/
467:     /*      ierr = MatView(snes->jacobian_pre,0); */

469:     PetscCall(KSPSetOperators(snes->ksp, jac_inact_inact, prejac_inact_inact));
470:     PetscCall(KSPSetUp(snes->ksp));
471:     {
472:       PC        pc;
473:       PetscBool flg;
474:       PetscCall(KSPGetPC(snes->ksp, &pc));
475:       PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &flg));
476:       if (flg) {
477:         KSP *subksps;
478:         PetscCall(PCFieldSplitGetSubKSP(pc, NULL, &subksps));
479:         PetscCall(KSPGetPC(subksps[0], &pc));
480:         PetscCall(PetscFree(subksps));
481:         PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCBJACOBI, &flg));
482:         if (flg) {
483:           PetscInt        n, N = 101 * 101, j, cnts[3] = {0, 0, 0};
484:           const PetscInt *ii;

486:           PetscCall(ISGetSize(vi->IS_inact, &n));
487:           PetscCall(ISGetIndices(vi->IS_inact, &ii));
488:           for (j = 0; j < n; j++) {
489:             if (ii[j] < N) cnts[0]++;
490:             else if (ii[j] < 2 * N) cnts[1]++;
491:             else if (ii[j] < 3 * N) cnts[2]++;
492:           }
493:           PetscCall(ISRestoreIndices(vi->IS_inact, &ii));

495:           PetscCall(PCBJacobiSetTotalBlocks(pc, 3, cnts));
496:         }
497:       }
498:     }

500:     PetscCall(KSPSolve(snes->ksp, F_inact, Y_inact));
501:     PetscCall(VecScatterBegin(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE));
502:     PetscCall(VecScatterEnd(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE));
503:     PetscCall(VecScatterBegin(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE));
504:     PetscCall(VecScatterEnd(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE));

506:     PetscCall(VecDestroy(&F_inact));
507:     PetscCall(VecDestroy(&Y_act));
508:     PetscCall(VecDestroy(&Y_inact));
509:     PetscCall(VecScatterDestroy(&scat_act));
510:     PetscCall(VecScatterDestroy(&scat_inact));
511:     PetscCall(ISDestroy(&IS_act));
512:     if (!isequal) {
513:       PetscCall(ISDestroy(&vi->IS_inact_prev));
514:       PetscCall(ISDuplicate(vi->IS_inact, &vi->IS_inact_prev));
515:     }
516:     PetscCall(ISDestroy(&vi->IS_inact));
517:     PetscCall(MatDestroy(&jac_inact_inact));
518:     if (snes->jacobian != snes->jacobian_pre) PetscCall(MatDestroy(&prejac_inact_inact));

520:     PetscCall(KSPGetConvergedReason(snes->ksp, &kspreason));
521:     if (kspreason < 0) {
522:       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
523:         PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", number linear solve failures %" PetscInt_FMT " greater than current SNES allowed, stopping solve\n", snes->iter, snes->numLinearSolveFailures));
524:         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
525:         break;
526:       }
527:     }

529:     PetscCall(KSPGetIterationNumber(snes->ksp, &lits));
530:     snes->linear_its += lits;
531:     PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits));
532:     /*
533:     if (snes->ops->precheck) {
534:       PetscBool changed_y = PETSC_FALSE;
535:       PetscUseTypeMethod(snes,precheck ,X,Y,snes->precheck,&changed_y);
536:     }

538:     if (PetscLogPrintInfo) PetscCall(SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W));
539:     */
540:     /* Compute a (scaled) negative update in the line search routine:
541:          Y <- X - lambda*Y
542:        and evaluate G = function(Y) (depends on the line search).
543:     */
544:     PetscCall(VecCopy(Y, snes->vec_sol_update));
545:     ynorm = 1;
546:     gnorm = fnorm;
547:     PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &gnorm, Y));
548:     PetscCall(DMDestroyVI(snes->dm));
549:     if (snes->reason) break;
550:     PetscCall(SNESLineSearchGetReason(snes->linesearch, &lsreason));
551:     if (lsreason) {
552:       if (snes->stol * xnorm > ynorm) {
553:         snes->reason = SNES_CONVERGED_SNORM_RELATIVE;
554:         break;
555:       } else if (lsreason == SNES_LINESEARCH_FAILED_FUNCTION_DOMAIN) {
556:         snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
557:         break;
558:       } else if (lsreason == SNES_LINESEARCH_FAILED_NANORINF) {
559:         snes->reason = SNES_DIVERGED_FUNCTION_NANORINF;
560:         break;
561:       } else if (lsreason == SNES_LINESEARCH_FAILED_OBJECTIVE_DOMAIN) {
562:         snes->reason = SNES_DIVERGED_OBJECTIVE_DOMAIN;
563:         break;
564:       } else if (lsreason == SNES_LINESEARCH_FAILED_JACOBIAN_DOMAIN) {
565:         snes->reason = SNES_DIVERGED_JACOBIAN_DOMAIN;
566:         break;
567:       } else if (++snes->numFailures >= snes->maxFailures) {
568:         PetscBool ismin;

570:         snes->reason = SNES_DIVERGED_LINE_SEARCH;
571:         PetscCall(SNESVICheckLocalMin_Private(snes, snes->jacobian, F, X, gnorm, &ismin));
572:         if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
573:         break;
574:       }
575:     }
576:     PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &gnorm, &ynorm));
577:     PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n", (double)fnorm, (double)gnorm, (double)ynorm, (int)lsreason));
578:     /* Update function and solution vectors */
579:     fnorm = gnorm;
580:     /* Monitor convergence */
581:     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
582:     snes->iter  = i + 1;
583:     snes->norm  = fnorm;
584:     snes->xnorm = xnorm;
585:     snes->ynorm = ynorm;
586:     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
587:     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, lits));
588:     /* Test for convergence, xnorm = || X || */
589:     if (snes->ops->converged != SNESConvergedSkip) PetscCall(VecNorm(X, NORM_2, &xnorm));
590:     PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
591:     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
592:     if (snes->reason) break;
593:   }
594:   /* make sure that the VI information attached to the DM is removed if the for loop above was broken early due to some exceptional conditional */
595:   PetscCall(DMDestroyVI(snes->dm));
596:   PetscFunctionReturn(PETSC_SUCCESS);
597: }

599: /*@C
600:   SNESVISetRedundancyCheck - Provide a function to check for any redundancy in the VI active set

602:   Logically Collective

604:   Input Parameters:
605: + snes - the `SNESVINEWTONRSLS` context
606: . func - the function to check of redundancies
607: - ctx  - optional context used by the function

609:   Calling sequence of func:
610: + snes      - the `SNES` context
611: . is_act    - the set of points in the active sets
612: . is_redact - output, the set of points in the non-redundant active set
613: - ctx       - optional context

615:   Level: advanced

617:   Note:
618:   Sometimes the inactive set will result in a singular sub-Jacobian problem that needs to be solved, this allows the user,
619:   when they know more about their specific problem to provide a function that removes the redundancy that results in the singular linear system

621:   See `SNESVINEWTONRSLS` for a concise description of the active and inactive sets

623: .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`, `DMSetVI()`
624:  @*/
625: PetscErrorCode SNESVISetRedundancyCheck(SNES snes, PetscErrorCode (*func)(SNES snes, IS is_act, IS *is_redact, PetscCtx ctx), PetscCtx ctx)
626: {
627:   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;

629:   PetscFunctionBegin;
631:   vi->checkredundancy = func;
632:   vi->ctxP            = ctx;
633:   PetscFunctionReturn(PETSC_SUCCESS);
634: }

636: #if PetscDefined(HAVE_MATLAB)
637:   #include <engine.h>
638:   #include <mex.h>
639: typedef struct {
640:   char    *funcname;
641:   mxArray *ctx;
642: } SNESMatlabContext;

644: PetscErrorCode SNESVIRedundancyCheck_Matlab(SNES snes, IS is_act, IS *is_redact, PetscCtx ctx)
645: {
646:   SNESMatlabContext *sctx = (SNESMatlabContext *)ctx;
647:   int                nlhs = 1, nrhs = 5;
648:   mxArray           *plhs[1], *prhs[5];
649:   long long int      l1 = 0, l2 = 0, ls = 0;
650:   PetscInt          *indices = NULL;

652:   PetscFunctionBegin;
655:   PetscAssertPointer(is_redact, 3);
656:   PetscCheckSameComm(snes, 1, is_act, 2);

658:   /* Create IS for reduced active set of size 0, its size and indices will
659:    bet set by the MATLAB function */
660:   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), 0, indices, PETSC_OWN_POINTER, is_redact));
661:   /* call MATLAB function in ctx */
662:   PetscCall(PetscArraycpy(&ls, &snes, 1));
663:   PetscCall(PetscArraycpy(&l1, &is_act, 1));
664:   PetscCall(PetscArraycpy(&l2, is_redact, 1));
665:   prhs[0] = mxCreateDoubleScalar((double)ls);
666:   prhs[1] = mxCreateDoubleScalar((double)l1);
667:   prhs[2] = mxCreateDoubleScalar((double)l2);
668:   prhs[3] = mxCreateString(sctx->funcname);
669:   prhs[4] = sctx->ctx;
670:   PetscCall(mexCallMATLAB(nlhs, plhs, nrhs, prhs, "PetscSNESVIRedundancyCheckInternal"));
671:   PetscCall(mxGetScalar(plhs[0]));
672:   mxDestroyArray(prhs[0]);
673:   mxDestroyArray(prhs[1]);
674:   mxDestroyArray(prhs[2]);
675:   mxDestroyArray(prhs[3]);
676:   mxDestroyArray(plhs[0]);
677:   PetscFunctionReturn(PETSC_SUCCESS);
678: }

680: PetscErrorCode SNESVISetRedundancyCheckMatlab(SNES snes, const char *func, mxArray *ctx)
681: {
682:   SNESMatlabContext *sctx;

684:   PetscFunctionBegin;
685:   /* currently sctx is memory bleed */
686:   PetscCall(PetscNew(&sctx));
687:   PetscCall(PetscStrallocpy(func, &sctx->funcname));
688:   sctx->ctx = mxDuplicateArray(ctx);
689:   PetscCall(SNESVISetRedundancyCheck(snes, SNESVIRedundancyCheck_Matlab, sctx));
690:   PetscFunctionReturn(PETSC_SUCCESS);
691: }

693: #endif

695: static PetscErrorCode SNESSetUp_VINEWTONRSLS(SNES snes)
696: {
697:   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
698:   PetscInt          *indices;
699:   PetscInt           i, n, rstart, rend;
700:   SNESLineSearch     linesearch;

702:   PetscFunctionBegin;
703:   PetscCall(SNESSetUp_VI(snes));

705:   /* Set up previous active index set for the first snes solve
706:    vi->IS_inact_prev = 0,1,2,....N */

708:   PetscCall(VecGetOwnershipRange(snes->work[0], &rstart, &rend));
709:   PetscCall(VecGetLocalSize(snes->work[0], &n));
710:   PetscCall(PetscMalloc1(n, &indices));
711:   for (i = 0; i < n; i++) indices[i] = rstart + i;
712:   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), n, indices, PETSC_OWN_POINTER, &vi->IS_inact_prev));

714:   /* set the line search functions */
715:   if (!snes->linesearch) {
716:     PetscCall(SNESGetLineSearch(snes, &linesearch));
717:     PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT));
718:   }
719:   PetscFunctionReturn(PETSC_SUCCESS);
720: }

722: static PetscErrorCode SNESReset_VINEWTONRSLS(SNES snes)
723: {
724:   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;

726:   PetscFunctionBegin;
727:   PetscCall(SNESReset_VI(snes));
728:   PetscCall(ISDestroy(&vi->IS_inact_prev));
729:   PetscFunctionReturn(PETSC_SUCCESS);
730: }

732: /*MC
733:    SNESVINEWTONRSLS - Reduced space active set solvers for variational inequalities based on Newton's method

735:    Options Database Keys:
736: +  -snes_type (vinewtonssls|vinewtonrsls) - A semi-smooth solver or a reduced space active set method
737: .  -snes_vi_zero_tolerance                - Tolerance for considering $u_i$ value to be on a bound.
738: .  -snes_vi_monitor                       - Prints the number of active constraints (inactive set points) at each iteration.
739: .  -snes_vi_monitor_residual              - View the residual vector at each iteration, using zero for active constraints (i.e. the inactive variables).
740: -  -snes_vi_monitor_active                - View the active set by outputting a one for vector components in the active set and zero for the inactive.

742:    Level: beginner

744:    Note:
745:    Reduced-space (active set methods) work as follows at each iteration\:
746:    - The algorithm produces an inactive set of variables, that is a list of variables whose values will not be changed in the current iteration, i.e. they
747:      are to be constrained to their current values. These are all the variables that are on the lower bound, that is $u_i = L_i$ with also $[F(u)]_i \ge 0$ or
748:      the upper bound $u_i = U_i$ with also $[F(u)]_i \le 0.$
749:    - A step direction is obtained by solving the linear system arising from the Jacobian used in Newton's method but with the inactive variables removed
750:      from both the rows and columns.
751:    - A line search is then used to update the active variables (the inactive set of variables are not changed).

753:    The inactive set is chosen based on the sign of $[F(u)]_i$ because this gives exactly the set of points that would be moved outside of the domain given
754:    an infinitesimal Newton (or even Richardson) step and our goal is to remain within the bounds, that is, to continue to satisfy the inequality constraints.

756:    See {cite}`benson2006flexible`

758: .seealso: [](ch_snes), `SNESVISetVariableBounds()`, `SNESVISetComputeVariableBounds()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESVINEWTONSSLS`, `SNESNEWTONTR`, `SNESLineSearchSetType()`, `SNESLineSearchSetPostCheck()`, `SNESLineSearchSetPreCheck()`, `SNESVIGetInactiveSet()`, `DMSetVI()`, `SNESVISetRedundancyCheck()`
759: M*/
760: PETSC_EXTERN PetscErrorCode SNESCreate_VINEWTONRSLS(SNES snes)
761: {
762:   SNES_VINEWTONRSLS *vi;
763:   SNESLineSearch     linesearch;

765:   PetscFunctionBegin;
766:   snes->ops->reset          = SNESReset_VINEWTONRSLS;
767:   snes->ops->setup          = SNESSetUp_VINEWTONRSLS;
768:   snes->ops->solve          = SNESSolve_VINEWTONRSLS;
769:   snes->ops->destroy        = SNESDestroy_VI;
770:   snes->ops->setfromoptions = SNESSetFromOptions_VI;
771:   snes->ops->view           = NULL;
772:   if (!snes->ops->converged || snes->ops->converged == SNESConvergedDefault) snes->ops->converged = SNESConvergedDefault_VI;

774:   snes->usesksp = PETSC_TRUE;
775:   snes->usesnpc = PETSC_FALSE;

777:   PetscCall(SNESGetLineSearch(snes, &linesearch));
778:   if (!((PetscObject)linesearch)->type_name) PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT));
779:   PetscCall(SNESLineSearchBTSetAlpha(linesearch, 0.0));

781:   snes->alwayscomputesfinalresidual = PETSC_TRUE;

783:   PetscCall(PetscNew(&vi));
784:   snes->data = (void *)vi;

786:   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESVISetVariableBounds_C", SNESVISetVariableBounds_VI));
787:   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESVISetComputeVariableBounds_C", SNESVISetComputeVariableBounds_VI));
788:   PetscFunctionReturn(PETSC_SUCCESS);
789: }