xref: /petsc/src/snes/impls/vi/rs/virs.c (revision f13dfd9ea68e0ddeee984e65c377a1819eab8a8a)
1 #include <../src/snes/impls/vi/rs/virsimpl.h> /*I "petscsnes.h" I*/
2 #include <petsc/private/dmimpl.h>
3 #include <petsc/private/vecimpl.h>
4 
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)
8 
9   Input Parameter:
10 . snes - the `SNES` context
11 
12   Output Parameter:
13 . inact - inactive set index set
14 
15   Level: advanced
16 
17 .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`
18 @*/
19 PetscErrorCode SNESVIGetInactiveSet(SNES snes, IS *inact)
20 {
21   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
22 
23   PetscFunctionBegin;
24   *inact = vi->IS_inact;
25   PetscFunctionReturn(PETSC_SUCCESS);
26 }
27 
28 /*
29     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
30   defined by the reduced space method.
31 
32     Simple calls the regular DM interpolation and restricts it to operation on the variables not associated with active constraints.
33 */
34 typedef struct {
35   PetscInt n; /* size of vectors in the reduced DM space */
36   IS       inactive;
37 
38   PetscErrorCode (*createinterpolation)(DM, DM, Mat *, Vec *); /* DM's original routines */
39   PetscErrorCode (*coarsen)(DM, MPI_Comm, DM *);
40   PetscErrorCode (*createglobalvector)(DM, Vec *);
41   PetscErrorCode (*createinjection)(DM, DM, Mat *);
42   PetscErrorCode (*hascreateinjection)(DM, PetscBool *);
43 
44   DM dm; /* when destroying this object we need to reset the above function into the base DM */
45 } DM_SNESVI;
46 
47 /*
48      DMCreateGlobalVector_SNESVI - Creates global vector of the size of the reduced space
49 */
50 static PetscErrorCode DMCreateGlobalVector_SNESVI(DM dm, Vec *vec)
51 {
52   PetscContainer isnes;
53   DM_SNESVI     *dmsnesvi;
54 
55   PetscFunctionBegin;
56   PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes));
57   PetscCheck(isnes, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Composed SNES is missing");
58   PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi));
59   PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)dm), dmsnesvi->n, PETSC_DETERMINE, vec));
60   PetscCall(VecSetDM(*vec, dm));
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*
65      DMCreateInterpolation_SNESVI - Modifieds the interpolation obtained from the DM by removing all rows and columns associated with active constraints.
66 */
67 static PetscErrorCode DMCreateInterpolation_SNESVI(DM dm1, DM dm2, Mat *mat, Vec *vec)
68 {
69   PetscContainer isnes;
70   DM_SNESVI     *dmsnesvi1, *dmsnesvi2;
71   Mat            interp;
72 
73   PetscFunctionBegin;
74   PetscCall(PetscObjectQuery((PetscObject)dm1, "VI", (PetscObject *)&isnes));
75   PetscCheck(isnes, PetscObjectComm((PetscObject)dm1), PETSC_ERR_PLIB, "Composed VI data structure is missing");
76   PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi1));
77   PetscCall(PetscObjectQuery((PetscObject)dm2, "VI", (PetscObject *)&isnes));
78   PetscCheck(isnes, PetscObjectComm((PetscObject)dm2), PETSC_ERR_PLIB, "Composed VI data structure is missing");
79   PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi2));
80 
81   PetscCall((*dmsnesvi1->createinterpolation)(dm1, dm2, &interp, NULL));
82   PetscCall(MatCreateSubMatrix(interp, dmsnesvi2->inactive, dmsnesvi1->inactive, MAT_INITIAL_MATRIX, mat));
83   PetscCall(MatDestroy(&interp));
84   *vec = NULL;
85   PetscFunctionReturn(PETSC_SUCCESS);
86 }
87 
88 /*
89      DMCoarsen_SNESVI - Computes the regular coarsened DM then computes additional information about its inactive set
90 */
91 static PetscErrorCode DMCoarsen_SNESVI(DM dm1, MPI_Comm comm, DM *dm2)
92 {
93   PetscContainer  isnes;
94   DM_SNESVI      *dmsnesvi1;
95   Vec             finemarked, coarsemarked;
96   IS              inactive;
97   Mat             inject;
98   const PetscInt *index;
99   PetscInt        n, k, cnt = 0, rstart, *coarseindex;
100   PetscScalar    *marked;
101 
102   PetscFunctionBegin;
103   PetscCall(PetscObjectQuery((PetscObject)dm1, "VI", (PetscObject *)&isnes));
104   PetscCheck(isnes, PetscObjectComm((PetscObject)dm1), PETSC_ERR_PLIB, "Composed VI data structure is missing");
105   PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi1));
106 
107   /* get the original coarsen */
108   PetscCall((*dmsnesvi1->coarsen)(dm1, comm, dm2));
109 
110   /* not sure why this extra reference is needed, but without the dm2 disappears too early */
111   /* Updating the KSPCreateVecs() to avoid using DMGetGlobalVector() when matrix is available removes the need for this reference? */
112   /*  PetscCall(PetscObjectReference((PetscObject)*dm2));*/
113 
114   /* need to set back global vectors in order to use the original injection */
115   PetscCall(DMClearGlobalVectors(dm1));
116 
117   dm1->ops->createglobalvector = dmsnesvi1->createglobalvector;
118 
119   PetscCall(DMCreateGlobalVector(dm1, &finemarked));
120   PetscCall(DMCreateGlobalVector(*dm2, &coarsemarked));
121 
122   /*
123      fill finemarked with locations of inactive points
124   */
125   PetscCall(ISGetIndices(dmsnesvi1->inactive, &index));
126   PetscCall(ISGetLocalSize(dmsnesvi1->inactive, &n));
127   PetscCall(VecSet(finemarked, 0.0));
128   for (k = 0; k < n; k++) PetscCall(VecSetValue(finemarked, index[k], 1.0, INSERT_VALUES));
129   PetscCall(VecAssemblyBegin(finemarked));
130   PetscCall(VecAssemblyEnd(finemarked));
131 
132   PetscCall(DMCreateInjection(*dm2, dm1, &inject));
133   PetscCall(MatRestrict(inject, finemarked, coarsemarked));
134   PetscCall(MatDestroy(&inject));
135 
136   /*
137      create index set list of coarse inactive points from coarsemarked
138   */
139   PetscCall(VecGetLocalSize(coarsemarked, &n));
140   PetscCall(VecGetOwnershipRange(coarsemarked, &rstart, NULL));
141   PetscCall(VecGetArray(coarsemarked, &marked));
142   for (k = 0; k < n; k++) {
143     if (marked[k] != 0.0) cnt++;
144   }
145   PetscCall(PetscMalloc1(cnt, &coarseindex));
146   cnt = 0;
147   for (k = 0; k < n; k++) {
148     if (marked[k] != 0.0) coarseindex[cnt++] = k + rstart;
149   }
150   PetscCall(VecRestoreArray(coarsemarked, &marked));
151   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)coarsemarked), cnt, coarseindex, PETSC_OWN_POINTER, &inactive));
152 
153   PetscCall(DMClearGlobalVectors(dm1));
154 
155   dm1->ops->createglobalvector = DMCreateGlobalVector_SNESVI;
156 
157   PetscCall(DMSetVI(*dm2, inactive));
158 
159   PetscCall(VecDestroy(&finemarked));
160   PetscCall(VecDestroy(&coarsemarked));
161   PetscCall(ISDestroy(&inactive));
162   PetscFunctionReturn(PETSC_SUCCESS);
163 }
164 
165 static PetscErrorCode DMDestroy_SNESVI(void *ctx)
166 {
167   DM_SNESVI *dmsnesvi = (DM_SNESVI *)ctx;
168 
169   PetscFunctionBegin;
170   /* reset the base methods in the DM object that were changed when the DM_SNESVI was reset */
171   dmsnesvi->dm->ops->createinterpolation = dmsnesvi->createinterpolation;
172   dmsnesvi->dm->ops->coarsen             = dmsnesvi->coarsen;
173   dmsnesvi->dm->ops->createglobalvector  = dmsnesvi->createglobalvector;
174   dmsnesvi->dm->ops->createinjection     = dmsnesvi->createinjection;
175   dmsnesvi->dm->ops->hascreateinjection  = dmsnesvi->hascreateinjection;
176   /* need to clear out this vectors because some of them may not have a reference to the DM
177     but they are counted as having references to the DM in DMDestroy() */
178   PetscCall(DMClearGlobalVectors(dmsnesvi->dm));
179 
180   PetscCall(ISDestroy(&dmsnesvi->inactive));
181   PetscCall(PetscFree(dmsnesvi));
182   PetscFunctionReturn(PETSC_SUCCESS);
183 }
184 
185 /*@
186   DMSetVI - Marks a `DM` as associated with a VI problem. This causes the interpolation/restriction operators to
187   be restricted to only those variables NOT associated with active constraints.
188 
189   Logically Collective
190 
191   Input Parameters:
192 + dm       - the `DM` object
193 - inactive - an `IS` indicating which points are currently not active
194 
195   Level: intermediate
196 
197 .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`
198 @*/
199 PetscErrorCode DMSetVI(DM dm, IS inactive)
200 {
201   PetscContainer isnes;
202   DM_SNESVI     *dmsnesvi;
203 
204   PetscFunctionBegin;
205   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
206 
207   PetscCall(PetscObjectReference((PetscObject)inactive));
208 
209   PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes));
210   if (!isnes) {
211     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)dm), &isnes));
212     PetscCall(PetscContainerSetUserDestroy(isnes, (PetscErrorCode(*)(void *))DMDestroy_SNESVI));
213     PetscCall(PetscNew(&dmsnesvi));
214     PetscCall(PetscContainerSetPointer(isnes, (void *)dmsnesvi));
215     PetscCall(PetscObjectCompose((PetscObject)dm, "VI", (PetscObject)isnes));
216     PetscCall(PetscContainerDestroy(&isnes));
217 
218     dmsnesvi->createinterpolation = dm->ops->createinterpolation;
219     dm->ops->createinterpolation  = DMCreateInterpolation_SNESVI;
220     dmsnesvi->coarsen             = dm->ops->coarsen;
221     dm->ops->coarsen              = DMCoarsen_SNESVI;
222     dmsnesvi->createglobalvector  = dm->ops->createglobalvector;
223     dm->ops->createglobalvector   = DMCreateGlobalVector_SNESVI;
224     dmsnesvi->createinjection     = dm->ops->createinjection;
225     dm->ops->createinjection      = NULL;
226     dmsnesvi->hascreateinjection  = dm->ops->hascreateinjection;
227     dm->ops->hascreateinjection   = NULL;
228   } else {
229     PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi));
230     PetscCall(ISDestroy(&dmsnesvi->inactive));
231   }
232   PetscCall(DMClearGlobalVectors(dm));
233   PetscCall(ISGetLocalSize(inactive, &dmsnesvi->n));
234 
235   dmsnesvi->inactive = inactive;
236   dmsnesvi->dm       = dm;
237   PetscFunctionReturn(PETSC_SUCCESS);
238 }
239 
240 /*
241      DMDestroyVI - Frees the DM_SNESVI object contained in the DM
242          - also resets the function pointers in the DM for createinterpolation() etc to use the original DM
243 */
244 PetscErrorCode DMDestroyVI(DM dm)
245 {
246   PetscFunctionBegin;
247   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
248   PetscCall(PetscObjectCompose((PetscObject)dm, "VI", (PetscObject)NULL));
249   PetscFunctionReturn(PETSC_SUCCESS);
250 }
251 
252 /* Create active and inactive set vectors. The local size of this vector is set and petsc computes the global size */
253 static PetscErrorCode SNESCreateSubVectors_VINEWTONRSLS(SNES snes, PetscInt n, Vec *newv)
254 {
255   Vec v;
256 
257   PetscFunctionBegin;
258   PetscCall(VecCreate(PetscObjectComm((PetscObject)snes), &v));
259   PetscCall(VecSetSizes(v, n, PETSC_DECIDE));
260   PetscCall(VecSetType(v, VECSTANDARD));
261   *newv = v;
262   PetscFunctionReturn(PETSC_SUCCESS);
263 }
264 
265 /* Resets the snes PC and KSP when the active set sizes change */
266 static PetscErrorCode SNESVIResetPCandKSP(SNES snes, Mat Amat, Mat Pmat)
267 {
268   KSP snesksp;
269 
270   PetscFunctionBegin;
271   PetscCall(SNESGetKSP(snes, &snesksp));
272   PetscCall(KSPReset(snesksp));
273   PetscCall(KSPResetFromOptions(snesksp));
274 
275   /*
276   KSP                    kspnew;
277   PC                     pcnew;
278   MatSolverType          stype;
279 
280   PetscCall(KSPCreate(PetscObjectComm((PetscObject)snes),&kspnew));
281   kspnew->pc_side = snesksp->pc_side;
282   kspnew->rtol    = snesksp->rtol;
283   kspnew->abstol    = snesksp->abstol;
284   kspnew->max_it  = snesksp->max_it;
285   PetscCall(KSPSetType(kspnew,((PetscObject)snesksp)->type_name));
286   PetscCall(KSPGetPC(kspnew,&pcnew));
287   PetscCall(PCSetType(kspnew->pc,((PetscObject)snesksp->pc)->type_name));
288   PetscCall(PCSetOperators(kspnew->pc,Amat,Pmat));
289   PetscCall(PCFactorGetMatSolverType(snesksp->pc,&stype));
290   PetscCall(PCFactorSetMatSolverType(kspnew->pc,stype));
291   PetscCall(KSPDestroy(&snesksp));
292   snes->ksp = kspnew;
293    PetscCall(KSPSetFromOptions(kspnew));*/
294   PetscFunctionReturn(PETSC_SUCCESS);
295 }
296 
297 /* Variational Inequality solver using reduce space method. No semismooth algorithm is
298    implemented in this algorithm. It basically identifies the active constraints and does
299    a linear solve on the other variables (those not associated with the active constraints). */
300 static PetscErrorCode SNESSolve_VINEWTONRSLS(SNES snes)
301 {
302   SNES_VINEWTONRSLS   *vi = (SNES_VINEWTONRSLS *)snes->data;
303   PetscInt             maxits, i, lits;
304   SNESLineSearchReason lssucceed;
305   PetscReal            fnorm, gnorm, xnorm = 0, ynorm;
306   Vec                  Y, X, F;
307   KSPConvergedReason   kspreason;
308   KSP                  ksp;
309   PC                   pc;
310 
311   PetscFunctionBegin;
312   /* Multigrid must use Galerkin for coarse grids with active set/reduced space methods; cannot rediscretize on coarser grids*/
313   PetscCall(SNESGetKSP(snes, &ksp));
314   PetscCall(KSPGetPC(ksp, &pc));
315   PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_BOTH));
316 
317   snes->numFailures            = 0;
318   snes->numLinearSolveFailures = 0;
319   snes->reason                 = SNES_CONVERGED_ITERATING;
320 
321   maxits = snes->max_its;  /* maximum number of iterations */
322   X      = snes->vec_sol;  /* solution vector */
323   F      = snes->vec_func; /* residual vector */
324   Y      = snes->work[0];  /* work vectors */
325 
326   PetscCall(SNESLineSearchSetVIFunctions(snes->linesearch, SNESVIProjectOntoBounds, SNESVIComputeInactiveSetFnorm));
327   PetscCall(SNESLineSearchSetVecs(snes->linesearch, X, NULL, NULL, NULL, NULL));
328   PetscCall(SNESLineSearchSetUp(snes->linesearch));
329 
330   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
331   snes->iter = 0;
332   snes->norm = 0.0;
333   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
334 
335   PetscCall(SNESVIProjectOntoBounds(snes, X));
336   PetscCall(SNESComputeFunction(snes, X, F));
337   PetscCall(SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm));
338   PetscCall(VecNorm(X, NORM_2, &xnorm)); /* xnorm <- ||x||  */
339   SNESCheckFunctionNorm(snes, fnorm);
340   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
341   snes->norm = fnorm;
342   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
343   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
344 
345   /* test convergence */
346   PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm));
347   PetscCall(SNESMonitor(snes, 0, fnorm));
348   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
349 
350   for (i = 0; i < maxits; i++) {
351     IS         IS_act;    /* _act -> active set _inact -> inactive set */
352     IS         IS_redact; /* redundant active set */
353     VecScatter scat_act, scat_inact;
354     PetscInt   nis_act, nis_inact;
355     Vec        Y_act, Y_inact, F_inact;
356     Mat        jac_inact_inact, prejac_inact_inact;
357     PetscBool  isequal;
358 
359     /* Call general purpose update function */
360     PetscTryTypeMethod(snes, update, snes->iter);
361     PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
362     SNESCheckJacobianDomainerror(snes);
363 
364     /* Create active and inactive index sets */
365 
366     /*original
367     PetscCall(SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&vi->IS_inact));
368      */
369     PetscCall(SNESVIGetActiveSetIS(snes, X, F, &IS_act));
370 
371     if (vi->checkredundancy) {
372       PetscCall((*vi->checkredundancy)(snes, IS_act, &IS_redact, vi->ctxP));
373       if (IS_redact) {
374         PetscCall(ISSort(IS_redact));
375         PetscCall(ISComplement(IS_redact, X->map->rstart, X->map->rend, &vi->IS_inact));
376         PetscCall(ISDestroy(&IS_redact));
377       } else {
378         PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact));
379       }
380     } else {
381       PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact));
382     }
383 
384     /* Create inactive set submatrix */
385     PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact));
386 
387     if (0) { /* Dead code (temporary developer hack) */
388       IS keptrows;
389       PetscCall(MatFindNonzeroRows(jac_inact_inact, &keptrows));
390       if (keptrows) {
391         PetscInt        cnt, *nrows, k;
392         const PetscInt *krows, *inact;
393         PetscInt        rstart;
394 
395         PetscCall(MatGetOwnershipRange(jac_inact_inact, &rstart, NULL));
396         PetscCall(MatDestroy(&jac_inact_inact));
397         PetscCall(ISDestroy(&IS_act));
398 
399         PetscCall(ISGetLocalSize(keptrows, &cnt));
400         PetscCall(ISGetIndices(keptrows, &krows));
401         PetscCall(ISGetIndices(vi->IS_inact, &inact));
402         PetscCall(PetscMalloc1(cnt, &nrows));
403         for (k = 0; k < cnt; k++) nrows[k] = inact[krows[k] - rstart];
404         PetscCall(ISRestoreIndices(keptrows, &krows));
405         PetscCall(ISRestoreIndices(vi->IS_inact, &inact));
406         PetscCall(ISDestroy(&keptrows));
407         PetscCall(ISDestroy(&vi->IS_inact));
408 
409         PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), cnt, nrows, PETSC_OWN_POINTER, &vi->IS_inact));
410         PetscCall(ISComplement(vi->IS_inact, F->map->rstart, F->map->rend, &IS_act));
411         PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact));
412       }
413     }
414     PetscCall(DMSetVI(snes->dm, vi->IS_inact));
415     /* remove later */
416 
417     /*
418     PetscCall(VecView(vi->xu,PETSC_VIEWER_BINARY_(((PetscObject)vi->xu)->comm)));
419     PetscCall(VecView(vi->xl,PETSC_VIEWER_BINARY_(((PetscObject)vi->xl)->comm)));
420     PetscCall(VecView(X,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)X))));
421     PetscCall(VecView(F,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)F))));
422     PetscCall(ISView(vi->IS_inact,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)vi->IS_inact))));
423      */
424 
425     /* Get sizes of active and inactive sets */
426     PetscCall(ISGetLocalSize(IS_act, &nis_act));
427     PetscCall(ISGetLocalSize(vi->IS_inact, &nis_inact));
428 
429     /* Create active and inactive set vectors */
430     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &F_inact));
431     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_act, &Y_act));
432     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &Y_inact));
433 
434     /* Create scatter contexts */
435     PetscCall(VecScatterCreate(Y, IS_act, Y_act, NULL, &scat_act));
436     PetscCall(VecScatterCreate(Y, vi->IS_inact, Y_inact, NULL, &scat_inact));
437 
438     /* Do a vec scatter to active and inactive set vectors */
439     PetscCall(VecScatterBegin(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD));
440     PetscCall(VecScatterEnd(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD));
441 
442     PetscCall(VecScatterBegin(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD));
443     PetscCall(VecScatterEnd(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD));
444 
445     PetscCall(VecScatterBegin(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD));
446     PetscCall(VecScatterEnd(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD));
447 
448     /* Active set direction = 0 */
449     PetscCall(VecSet(Y_act, 0));
450     if (snes->jacobian != snes->jacobian_pre) {
451       PetscCall(MatCreateSubMatrix(snes->jacobian_pre, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &prejac_inact_inact));
452     } else prejac_inact_inact = jac_inact_inact;
453 
454     PetscCall(ISEqual(vi->IS_inact_prev, vi->IS_inact, &isequal));
455     if (!isequal) {
456       PetscCall(SNESVIResetPCandKSP(snes, jac_inact_inact, prejac_inact_inact));
457       PetscCall(PCFieldSplitRestrictIS(pc, vi->IS_inact));
458     }
459 
460     /*      PetscCall(ISView(vi->IS_inact,0)); */
461     /*      PetscCall(ISView(IS_act,0));*/
462     /*      ierr = MatView(snes->jacobian_pre,0); */
463 
464     PetscCall(KSPSetOperators(snes->ksp, jac_inact_inact, prejac_inact_inact));
465     PetscCall(KSPSetUp(snes->ksp));
466     {
467       PC        pc;
468       PetscBool flg;
469       PetscCall(KSPGetPC(snes->ksp, &pc));
470       PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &flg));
471       if (flg) {
472         KSP *subksps;
473         PetscCall(PCFieldSplitGetSubKSP(pc, NULL, &subksps));
474         PetscCall(KSPGetPC(subksps[0], &pc));
475         PetscCall(PetscFree(subksps));
476         PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCBJACOBI, &flg));
477         if (flg) {
478           PetscInt        n, N = 101 * 101, j, cnts[3] = {0, 0, 0};
479           const PetscInt *ii;
480 
481           PetscCall(ISGetSize(vi->IS_inact, &n));
482           PetscCall(ISGetIndices(vi->IS_inact, &ii));
483           for (j = 0; j < n; j++) {
484             if (ii[j] < N) cnts[0]++;
485             else if (ii[j] < 2 * N) cnts[1]++;
486             else if (ii[j] < 3 * N) cnts[2]++;
487           }
488           PetscCall(ISRestoreIndices(vi->IS_inact, &ii));
489 
490           PetscCall(PCBJacobiSetTotalBlocks(pc, 3, cnts));
491         }
492       }
493     }
494 
495     PetscCall(KSPSolve(snes->ksp, F_inact, Y_inact));
496     PetscCall(VecScatterBegin(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE));
497     PetscCall(VecScatterEnd(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE));
498     PetscCall(VecScatterBegin(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE));
499     PetscCall(VecScatterEnd(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE));
500 
501     PetscCall(VecDestroy(&F_inact));
502     PetscCall(VecDestroy(&Y_act));
503     PetscCall(VecDestroy(&Y_inact));
504     PetscCall(VecScatterDestroy(&scat_act));
505     PetscCall(VecScatterDestroy(&scat_inact));
506     PetscCall(ISDestroy(&IS_act));
507     if (!isequal) {
508       PetscCall(ISDestroy(&vi->IS_inact_prev));
509       PetscCall(ISDuplicate(vi->IS_inact, &vi->IS_inact_prev));
510     }
511     PetscCall(ISDestroy(&vi->IS_inact));
512     PetscCall(MatDestroy(&jac_inact_inact));
513     if (snes->jacobian != snes->jacobian_pre) PetscCall(MatDestroy(&prejac_inact_inact));
514 
515     PetscCall(KSPGetConvergedReason(snes->ksp, &kspreason));
516     if (kspreason < 0) {
517       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
518         PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", number linear solve failures %" PetscInt_FMT " greater than current SNES allowed, stopping solve\n", snes->iter, snes->numLinearSolveFailures));
519         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
520         break;
521       }
522     }
523 
524     PetscCall(KSPGetIterationNumber(snes->ksp, &lits));
525     snes->linear_its += lits;
526     PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits));
527     /*
528     if (snes->ops->precheck) {
529       PetscBool changed_y = PETSC_FALSE;
530       PetscUseTypeMethod(snes,precheck ,X,Y,snes->precheck,&changed_y);
531     }
532 
533     if (PetscLogPrintInfo) PetscCall(SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W));
534     */
535     /* Compute a (scaled) negative update in the line search routine:
536          Y <- X - lambda*Y
537        and evaluate G = function(Y) (depends on the line search).
538     */
539     PetscCall(VecCopy(Y, snes->vec_sol_update));
540     ynorm = 1;
541     gnorm = fnorm;
542     PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &gnorm, Y));
543     PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed));
544     PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &gnorm, &ynorm));
545     PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n", (double)fnorm, (double)gnorm, (double)ynorm, (int)lssucceed));
546     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
547     if (snes->domainerror) {
548       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
549       PetscCall(DMDestroyVI(snes->dm));
550       PetscFunctionReturn(PETSC_SUCCESS);
551     }
552     if (lssucceed) {
553       if (++snes->numFailures >= snes->maxFailures) {
554         PetscBool ismin;
555         snes->reason = SNES_DIVERGED_LINE_SEARCH;
556         PetscCall(SNESVICheckLocalMin_Private(snes, snes->jacobian, F, X, gnorm, &ismin));
557         if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
558         break;
559       }
560     }
561     PetscCall(DMDestroyVI(snes->dm));
562     /* Update function and solution vectors */
563     fnorm = gnorm;
564     /* Monitor convergence */
565     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
566     snes->iter  = i + 1;
567     snes->norm  = fnorm;
568     snes->xnorm = xnorm;
569     snes->ynorm = ynorm;
570     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
571     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, lits));
572     /* Test for convergence, xnorm = || X || */
573     if (snes->ops->converged != SNESConvergedSkip) PetscCall(VecNorm(X, NORM_2, &xnorm));
574     PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
575     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
576     if (snes->reason) break;
577   }
578   /* 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 */
579   PetscCall(DMDestroyVI(snes->dm));
580   PetscFunctionReturn(PETSC_SUCCESS);
581 }
582 
583 /*@C
584   SNESVISetRedundancyCheck - Provide a function to check for any redundancy in the VI active set
585 
586   Logically Collective
587 
588   Input Parameters:
589 + snes - the `SNESVINEWTONRSLS` context
590 . func - the function to check of redundancies
591 - ctx  - optional context used by the function
592 
593   Level: advanced
594 
595   Note:
596   Sometimes the inactive set will result in a singular sub-Jacobian problem that needs to be solved, this allows the user,
597   when they know more about their specific problem to provide a function that removes the redundancy that results in the singular linear system
598 
599 .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`, `DMSetVI()`
600  @*/
601 PetscErrorCode SNESVISetRedundancyCheck(SNES snes, PetscErrorCode (*func)(SNES, IS, IS *, void *), void *ctx)
602 {
603   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
604 
605   PetscFunctionBegin;
606   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
607   vi->checkredundancy = func;
608   vi->ctxP            = ctx;
609   PetscFunctionReturn(PETSC_SUCCESS);
610 }
611 
612 #if defined(PETSC_HAVE_MATLAB)
613   #include <engine.h>
614   #include <mex.h>
615 typedef struct {
616   char    *funcname;
617   mxArray *ctx;
618 } SNESMatlabContext;
619 
620 PetscErrorCode SNESVIRedundancyCheck_Matlab(SNES snes, IS is_act, IS *is_redact, void *ctx)
621 {
622   SNESMatlabContext *sctx = (SNESMatlabContext *)ctx;
623   int                nlhs = 1, nrhs = 5;
624   mxArray           *plhs[1], *prhs[5];
625   long long int      l1 = 0, l2 = 0, ls = 0;
626   PetscInt          *indices = NULL;
627 
628   PetscFunctionBegin;
629   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
630   PetscValidHeaderSpecific(is_act, IS_CLASSID, 2);
631   PetscAssertPointer(is_redact, 3);
632   PetscCheckSameComm(snes, 1, is_act, 2);
633 
634   /* Create IS for reduced active set of size 0, its size and indices will
635    bet set by the MATLAB function */
636   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), 0, indices, PETSC_OWN_POINTER, is_redact));
637   /* call MATLAB function in ctx */
638   PetscCall(PetscArraycpy(&ls, &snes, 1));
639   PetscCall(PetscArraycpy(&l1, &is_act, 1));
640   PetscCall(PetscArraycpy(&l2, is_redact, 1));
641   prhs[0] = mxCreateDoubleScalar((double)ls);
642   prhs[1] = mxCreateDoubleScalar((double)l1);
643   prhs[2] = mxCreateDoubleScalar((double)l2);
644   prhs[3] = mxCreateString(sctx->funcname);
645   prhs[4] = sctx->ctx;
646   PetscCall(mexCallMATLAB(nlhs, plhs, nrhs, prhs, "PetscSNESVIRedundancyCheckInternal"));
647   PetscCall(mxGetScalar(plhs[0]));
648   mxDestroyArray(prhs[0]);
649   mxDestroyArray(prhs[1]);
650   mxDestroyArray(prhs[2]);
651   mxDestroyArray(prhs[3]);
652   mxDestroyArray(plhs[0]);
653   PetscFunctionReturn(PETSC_SUCCESS);
654 }
655 
656 PetscErrorCode SNESVISetRedundancyCheckMatlab(SNES snes, const char *func, mxArray *ctx)
657 {
658   SNESMatlabContext *sctx;
659 
660   PetscFunctionBegin;
661   /* currently sctx is memory bleed */
662   PetscCall(PetscNew(&sctx));
663   PetscCall(PetscStrallocpy(func, &sctx->funcname));
664   sctx->ctx = mxDuplicateArray(ctx);
665   PetscCall(SNESVISetRedundancyCheck(snes, SNESVIRedundancyCheck_Matlab, sctx));
666   PetscFunctionReturn(PETSC_SUCCESS);
667 }
668 
669 #endif
670 
671 static PetscErrorCode SNESSetUp_VINEWTONRSLS(SNES snes)
672 {
673   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
674   PetscInt          *indices;
675   PetscInt           i, n, rstart, rend;
676   SNESLineSearch     linesearch;
677 
678   PetscFunctionBegin;
679   PetscCall(SNESSetUp_VI(snes));
680 
681   /* Set up previous active index set for the first snes solve
682    vi->IS_inact_prev = 0,1,2,....N */
683 
684   PetscCall(VecGetOwnershipRange(snes->vec_sol, &rstart, &rend));
685   PetscCall(VecGetLocalSize(snes->vec_sol, &n));
686   PetscCall(PetscMalloc1(n, &indices));
687   for (i = 0; i < n; i++) indices[i] = rstart + i;
688   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), n, indices, PETSC_OWN_POINTER, &vi->IS_inact_prev));
689 
690   /* set the line search functions */
691   if (!snes->linesearch) {
692     PetscCall(SNESGetLineSearch(snes, &linesearch));
693     PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT));
694   }
695   PetscFunctionReturn(PETSC_SUCCESS);
696 }
697 
698 static PetscErrorCode SNESReset_VINEWTONRSLS(SNES snes)
699 {
700   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
701 
702   PetscFunctionBegin;
703   PetscCall(SNESReset_VI(snes));
704   PetscCall(ISDestroy(&vi->IS_inact_prev));
705   PetscFunctionReturn(PETSC_SUCCESS);
706 }
707 
708 /*MC
709       SNESVINEWTONRSLS - Reduced space active set solvers for variational inequalities based on Newton's method
710 
711    Options Database Keys:
712 +   -snes_type <vinewtonssls,vinewtonrsls> - a semi-smooth solver or a reduced space active set method
713 -   -snes_vi_monitor                       - prints the number of active constraints at each iteration.
714 
715    Level: beginner
716 
717    Note:
718    At each set of this methods the algorithm produces an inactive set of variables that are constrained to their current values
719    (because changing these values would result in those variables no longer satisfying the inequality constraints)
720    and produces a step direction by solving the linear system arising from the Jacobian with the inactive variables removed. In other
721    words on a reduced space of the solution space. Based on the Newton update it then adjusts the inactive set for the next iteration.
722 
723    See {cite}`benson2006flexible`
724 
725 .seealso: [](ch_snes), `SNESVISetVariableBounds()`, `SNESVISetComputeVariableBounds()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESVINEWTONSSLS`, `SNESNEWTONTR`, `SNESLineSearchSetType()`, `SNESLineSearchSetPostCheck()`, `SNESLineSearchSetPreCheck()`, `SNESVIGetInactiveSet()`, `DMSetVI()`, `SNESVISetRedundancyCheck()`
726 M*/
727 PETSC_EXTERN PetscErrorCode SNESCreate_VINEWTONRSLS(SNES snes)
728 {
729   SNES_VINEWTONRSLS *vi;
730   SNESLineSearch     linesearch;
731 
732   PetscFunctionBegin;
733   snes->ops->reset          = SNESReset_VINEWTONRSLS;
734   snes->ops->setup          = SNESSetUp_VINEWTONRSLS;
735   snes->ops->solve          = SNESSolve_VINEWTONRSLS;
736   snes->ops->destroy        = SNESDestroy_VI;
737   snes->ops->setfromoptions = SNESSetFromOptions_VI;
738   snes->ops->view           = NULL;
739   snes->ops->converged      = SNESConvergedDefault_VI;
740 
741   snes->usesksp = PETSC_TRUE;
742   snes->usesnpc = PETSC_FALSE;
743 
744   PetscCall(SNESGetLineSearch(snes, &linesearch));
745   if (!((PetscObject)linesearch)->type_name) PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT));
746   PetscCall(SNESLineSearchBTSetAlpha(linesearch, 0.0));
747 
748   snes->alwayscomputesfinalresidual = PETSC_TRUE;
749 
750   PetscCall(PetscNew(&vi));
751   snes->data          = (void *)vi;
752   vi->checkredundancy = NULL;
753 
754   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESVISetVariableBounds_C", SNESVISetVariableBounds_VI));
755   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESVISetComputeVariableBounds_C", SNESVISetComputeVariableBounds_VI));
756   PetscFunctionReturn(PETSC_SUCCESS);
757 }
758