xref: /petsc/src/snes/impls/vi/rs/virs.c (revision 517f4e3460cd8c7e73c19b8bf533f1efe47f30a7)
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: `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(DM_SNESVI *dmsnesvi)
166 {
167   PetscFunctionBegin;
168   /* reset the base methods in the DM object that were changed when the DM_SNESVI was reset */
169   dmsnesvi->dm->ops->createinterpolation = dmsnesvi->createinterpolation;
170   dmsnesvi->dm->ops->coarsen             = dmsnesvi->coarsen;
171   dmsnesvi->dm->ops->createglobalvector  = dmsnesvi->createglobalvector;
172   dmsnesvi->dm->ops->createinjection     = dmsnesvi->createinjection;
173   dmsnesvi->dm->ops->hascreateinjection  = dmsnesvi->hascreateinjection;
174   /* need to clear out this vectors because some of them may not have a reference to the DM
175     but they are counted as having references to the DM in DMDestroy() */
176   PetscCall(DMClearGlobalVectors(dmsnesvi->dm));
177 
178   PetscCall(ISDestroy(&dmsnesvi->inactive));
179   PetscCall(PetscFree(dmsnesvi));
180   PetscFunctionReturn(PETSC_SUCCESS);
181 }
182 
183 /*@
184   DMSetVI - Marks a DM as associated with a VI problem. This causes the interpolation/restriction operators to
185   be restricted to only those variables NOT associated with active constraints.
186 
187   Logically Collective
188 
189   Input Parameters:
190 + dm       - the `DM` object
191 - inactive - an `IS` indicating which points are currently not active
192 
193   Level: intermediate
194 
195 .seealso: `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`
196 @*/
197 PetscErrorCode DMSetVI(DM dm, IS inactive)
198 {
199   PetscContainer isnes;
200   DM_SNESVI     *dmsnesvi;
201 
202   PetscFunctionBegin;
203   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
204 
205   PetscCall(PetscObjectReference((PetscObject)inactive));
206 
207   PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes));
208   if (!isnes) {
209     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)dm), &isnes));
210     PetscCall(PetscContainerSetUserDestroy(isnes, (PetscErrorCode(*)(void *))DMDestroy_SNESVI));
211     PetscCall(PetscNew(&dmsnesvi));
212     PetscCall(PetscContainerSetPointer(isnes, (void *)dmsnesvi));
213     PetscCall(PetscObjectCompose((PetscObject)dm, "VI", (PetscObject)isnes));
214     PetscCall(PetscContainerDestroy(&isnes));
215 
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, (void **)&dmsnesvi));
228     PetscCall(ISDestroy(&dmsnesvi->inactive));
229   }
230   PetscCall(DMClearGlobalVectors(dm));
231   PetscCall(ISGetLocalSize(inactive, &dmsnesvi->n));
232 
233   dmsnesvi->inactive = inactive;
234   dmsnesvi->dm       = dm;
235   PetscFunctionReturn(PETSC_SUCCESS);
236 }
237 
238 /*
239      DMDestroyVI - Frees the DM_SNESVI object contained in the DM
240          - also resets the function pointers in the DM for createinterpolation() etc to use the original DM
241 */
242 PetscErrorCode DMDestroyVI(DM dm)
243 {
244   PetscFunctionBegin;
245   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
246   PetscCall(PetscObjectCompose((PetscObject)dm, "VI", (PetscObject)NULL));
247   PetscFunctionReturn(PETSC_SUCCESS);
248 }
249 
250 /* Create active and inactive set vectors. The local size of this vector is set and petsc computes the global size */
251 static PetscErrorCode SNESCreateSubVectors_VINEWTONRSLS(SNES snes, PetscInt n, Vec *newv)
252 {
253   Vec v;
254 
255   PetscFunctionBegin;
256   PetscCall(VecCreate(PetscObjectComm((PetscObject)snes), &v));
257   PetscCall(VecSetSizes(v, n, PETSC_DECIDE));
258   PetscCall(VecSetType(v, VECSTANDARD));
259   *newv = v;
260   PetscFunctionReturn(PETSC_SUCCESS);
261 }
262 
263 /* Resets the snes PC and KSP when the active set sizes change */
264 static PetscErrorCode SNESVIResetPCandKSP(SNES snes, Mat Amat, Mat Pmat)
265 {
266   KSP snesksp;
267 
268   PetscFunctionBegin;
269   PetscCall(SNESGetKSP(snes, &snesksp));
270   PetscCall(KSPReset(snesksp));
271   PetscCall(KSPResetFromOptions(snesksp));
272 
273   /*
274   KSP                    kspnew;
275   PC                     pcnew;
276   MatSolverType          stype;
277 
278   PetscCall(KSPCreate(PetscObjectComm((PetscObject)snes),&kspnew));
279   kspnew->pc_side = snesksp->pc_side;
280   kspnew->rtol    = snesksp->rtol;
281   kspnew->abstol    = snesksp->abstol;
282   kspnew->max_it  = snesksp->max_it;
283   PetscCall(KSPSetType(kspnew,((PetscObject)snesksp)->type_name));
284   PetscCall(KSPGetPC(kspnew,&pcnew));
285   PetscCall(PCSetType(kspnew->pc,((PetscObject)snesksp->pc)->type_name));
286   PetscCall(PCSetOperators(kspnew->pc,Amat,Pmat));
287   PetscCall(PCFactorGetMatSolverType(snesksp->pc,&stype));
288   PetscCall(PCFactorSetMatSolverType(kspnew->pc,stype));
289   PetscCall(KSPDestroy(&snesksp));
290   snes->ksp = kspnew;
291    PetscCall(KSPSetFromOptions(kspnew));*/
292   PetscFunctionReturn(PETSC_SUCCESS);
293 }
294 
295 /* Variational Inequality solver using reduce space method. No semismooth algorithm is
296    implemented in this algorithm. It basically identifies the active constraints and does
297    a linear solve on the other variables (those not associated with the active constraints). */
298 static PetscErrorCode SNESSolve_VINEWTONRSLS(SNES snes)
299 {
300   SNES_VINEWTONRSLS   *vi = (SNES_VINEWTONRSLS *)snes->data;
301   PetscInt             maxits, i, lits;
302   SNESLineSearchReason lssucceed;
303   PetscReal            fnorm, gnorm, xnorm = 0, ynorm;
304   Vec                  Y, X, F;
305   KSPConvergedReason   kspreason;
306   KSP                  ksp;
307   PC                   pc;
308 
309   PetscFunctionBegin;
310   /* Multigrid must use Galerkin for coarse grids with active set/reduced space methods; cannot rediscretize on coarser grids*/
311   PetscCall(SNESGetKSP(snes, &ksp));
312   PetscCall(KSPGetPC(ksp, &pc));
313   PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_BOTH));
314 
315   snes->numFailures            = 0;
316   snes->numLinearSolveFailures = 0;
317   snes->reason                 = SNES_CONVERGED_ITERATING;
318 
319   maxits = snes->max_its;  /* maximum number of iterations */
320   X      = snes->vec_sol;  /* solution vector */
321   F      = snes->vec_func; /* residual vector */
322   Y      = snes->work[0];  /* work vectors */
323 
324   PetscCall(SNESLineSearchSetVIFunctions(snes->linesearch, SNESVIProjectOntoBounds, SNESVIComputeInactiveSetFnorm));
325   PetscCall(SNESLineSearchSetVecs(snes->linesearch, X, NULL, NULL, NULL, NULL));
326   PetscCall(SNESLineSearchSetUp(snes->linesearch));
327 
328   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
329   snes->iter = 0;
330   snes->norm = 0.0;
331   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
332 
333   PetscCall(SNESVIProjectOntoBounds(snes, X));
334   PetscCall(SNESComputeFunction(snes, X, F));
335   PetscCall(SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm));
336   PetscCall(VecNorm(X, NORM_2, &xnorm)); /* xnorm <- ||x||  */
337   SNESCheckFunctionNorm(snes, fnorm);
338   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
339   snes->norm = fnorm;
340   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
341   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
342 
343   /* test convergence */
344   PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm));
345   PetscCall(SNESMonitor(snes, 0, fnorm));
346   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
347 
348   for (i = 0; i < maxits; i++) {
349     IS         IS_act;    /* _act -> active set _inact -> inactive set */
350     IS         IS_redact; /* redundant active set */
351     VecScatter scat_act, scat_inact;
352     PetscInt   nis_act, nis_inact;
353     Vec        Y_act, Y_inact, F_inact;
354     Mat        jac_inact_inact, prejac_inact_inact;
355     PetscBool  isequal;
356 
357     /* Call general purpose update function */
358     PetscTryTypeMethod(snes, update, snes->iter);
359     PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre));
360     SNESCheckJacobianDomainerror(snes);
361 
362     /* Create active and inactive index sets */
363 
364     /*original
365     PetscCall(SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&vi->IS_inact));
366      */
367     PetscCall(SNESVIGetActiveSetIS(snes, X, F, &IS_act));
368 
369     if (vi->checkredundancy) {
370       PetscCall((*vi->checkredundancy)(snes, IS_act, &IS_redact, vi->ctxP));
371       if (IS_redact) {
372         PetscCall(ISSort(IS_redact));
373         PetscCall(ISComplement(IS_redact, X->map->rstart, X->map->rend, &vi->IS_inact));
374         PetscCall(ISDestroy(&IS_redact));
375       } else {
376         PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact));
377       }
378     } else {
379       PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact));
380     }
381 
382     /* Create inactive set submatrix */
383     PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact));
384 
385     if (0) { /* Dead code (temporary developer hack) */
386       IS keptrows;
387       PetscCall(MatFindNonzeroRows(jac_inact_inact, &keptrows));
388       if (keptrows) {
389         PetscInt        cnt, *nrows, k;
390         const PetscInt *krows, *inact;
391         PetscInt        rstart;
392 
393         PetscCall(MatGetOwnershipRange(jac_inact_inact, &rstart, NULL));
394         PetscCall(MatDestroy(&jac_inact_inact));
395         PetscCall(ISDestroy(&IS_act));
396 
397         PetscCall(ISGetLocalSize(keptrows, &cnt));
398         PetscCall(ISGetIndices(keptrows, &krows));
399         PetscCall(ISGetIndices(vi->IS_inact, &inact));
400         PetscCall(PetscMalloc1(cnt, &nrows));
401         for (k = 0; k < cnt; k++) nrows[k] = inact[krows[k] - rstart];
402         PetscCall(ISRestoreIndices(keptrows, &krows));
403         PetscCall(ISRestoreIndices(vi->IS_inact, &inact));
404         PetscCall(ISDestroy(&keptrows));
405         PetscCall(ISDestroy(&vi->IS_inact));
406 
407         PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), cnt, nrows, PETSC_OWN_POINTER, &vi->IS_inact));
408         PetscCall(ISComplement(vi->IS_inact, F->map->rstart, F->map->rend, &IS_act));
409         PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact));
410       }
411     }
412     PetscCall(DMSetVI(snes->dm, vi->IS_inact));
413     /* remove later */
414 
415     /*
416     PetscCall(VecView(vi->xu,PETSC_VIEWER_BINARY_(((PetscObject)(vi->xu))->comm)));
417     PetscCall(VecView(vi->xl,PETSC_VIEWER_BINARY_(((PetscObject)(vi->xl))->comm)));
418     PetscCall(VecView(X,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)X))));
419     PetscCall(VecView(F,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)F))));
420     PetscCall(ISView(vi->IS_inact,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)vi->IS_inact))));
421      */
422 
423     /* Get sizes of active and inactive sets */
424     PetscCall(ISGetLocalSize(IS_act, &nis_act));
425     PetscCall(ISGetLocalSize(vi->IS_inact, &nis_inact));
426 
427     /* Create active and inactive set vectors */
428     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &F_inact));
429     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_act, &Y_act));
430     PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &Y_inact));
431 
432     /* Create scatter contexts */
433     PetscCall(VecScatterCreate(Y, IS_act, Y_act, NULL, &scat_act));
434     PetscCall(VecScatterCreate(Y, vi->IS_inact, Y_inact, NULL, &scat_inact));
435 
436     /* Do a vec scatter to active and inactive set vectors */
437     PetscCall(VecScatterBegin(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD));
438     PetscCall(VecScatterEnd(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD));
439 
440     PetscCall(VecScatterBegin(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD));
441     PetscCall(VecScatterEnd(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD));
442 
443     PetscCall(VecScatterBegin(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD));
444     PetscCall(VecScatterEnd(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD));
445 
446     /* Active set direction = 0 */
447     PetscCall(VecSet(Y_act, 0));
448     if (snes->jacobian != snes->jacobian_pre) {
449       PetscCall(MatCreateSubMatrix(snes->jacobian_pre, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &prejac_inact_inact));
450     } else prejac_inact_inact = jac_inact_inact;
451 
452     PetscCall(ISEqual(vi->IS_inact_prev, vi->IS_inact, &isequal));
453     if (!isequal) {
454       PetscCall(SNESVIResetPCandKSP(snes, jac_inact_inact, prejac_inact_inact));
455       PetscCall(PCFieldSplitRestrictIS(pc, vi->IS_inact));
456     }
457 
458     /*      PetscCall(ISView(vi->IS_inact,0)); */
459     /*      PetscCall(ISView(IS_act,0));*/
460     /*      ierr = MatView(snes->jacobian_pre,0); */
461 
462     PetscCall(KSPSetOperators(snes->ksp, jac_inact_inact, prejac_inact_inact));
463     PetscCall(KSPSetUp(snes->ksp));
464     {
465       PC        pc;
466       PetscBool flg;
467       PetscCall(KSPGetPC(snes->ksp, &pc));
468       PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &flg));
469       if (flg) {
470         KSP *subksps;
471         PetscCall(PCFieldSplitGetSubKSP(pc, NULL, &subksps));
472         PetscCall(KSPGetPC(subksps[0], &pc));
473         PetscCall(PetscFree(subksps));
474         PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCBJACOBI, &flg));
475         if (flg) {
476           PetscInt        n, N = 101 * 101, j, cnts[3] = {0, 0, 0};
477           const PetscInt *ii;
478 
479           PetscCall(ISGetSize(vi->IS_inact, &n));
480           PetscCall(ISGetIndices(vi->IS_inact, &ii));
481           for (j = 0; j < n; j++) {
482             if (ii[j] < N) cnts[0]++;
483             else if (ii[j] < 2 * N) cnts[1]++;
484             else if (ii[j] < 3 * N) cnts[2]++;
485           }
486           PetscCall(ISRestoreIndices(vi->IS_inact, &ii));
487 
488           PetscCall(PCBJacobiSetTotalBlocks(pc, 3, cnts));
489         }
490       }
491     }
492 
493     PetscCall(KSPSolve(snes->ksp, F_inact, Y_inact));
494     PetscCall(VecScatterBegin(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE));
495     PetscCall(VecScatterEnd(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE));
496     PetscCall(VecScatterBegin(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE));
497     PetscCall(VecScatterEnd(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE));
498 
499     PetscCall(VecDestroy(&F_inact));
500     PetscCall(VecDestroy(&Y_act));
501     PetscCall(VecDestroy(&Y_inact));
502     PetscCall(VecScatterDestroy(&scat_act));
503     PetscCall(VecScatterDestroy(&scat_inact));
504     PetscCall(ISDestroy(&IS_act));
505     if (!isequal) {
506       PetscCall(ISDestroy(&vi->IS_inact_prev));
507       PetscCall(ISDuplicate(vi->IS_inact, &vi->IS_inact_prev));
508     }
509     PetscCall(ISDestroy(&vi->IS_inact));
510     PetscCall(MatDestroy(&jac_inact_inact));
511     if (snes->jacobian != snes->jacobian_pre) PetscCall(MatDestroy(&prejac_inact_inact));
512 
513     PetscCall(KSPGetConvergedReason(snes->ksp, &kspreason));
514     if (kspreason < 0) {
515       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
516         PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", number linear solve failures %" PetscInt_FMT " greater than current SNES allowed, stopping solve\n", snes->iter, snes->numLinearSolveFailures));
517         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
518         break;
519       }
520     }
521 
522     PetscCall(KSPGetIterationNumber(snes->ksp, &lits));
523     snes->linear_its += lits;
524     PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits));
525     /*
526     if (snes->ops->precheck) {
527       PetscBool changed_y = PETSC_FALSE;
528       PetscUseTypeMethod(snes,precheck ,X,Y,snes->precheck,&changed_y);
529     }
530 
531     if (PetscLogPrintInfo) PetscCall(SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W));
532     */
533     /* Compute a (scaled) negative update in the line search routine:
534          Y <- X - lambda*Y
535        and evaluate G = function(Y) (depends on the line search).
536     */
537     PetscCall(VecCopy(Y, snes->vec_sol_update));
538     ynorm = 1;
539     gnorm = fnorm;
540     PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &gnorm, Y));
541     PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed));
542     PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &gnorm, &ynorm));
543     PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n", (double)fnorm, (double)gnorm, (double)ynorm, (int)lssucceed));
544     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
545     if (snes->domainerror) {
546       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
547       PetscCall(DMDestroyVI(snes->dm));
548       PetscFunctionReturn(PETSC_SUCCESS);
549     }
550     if (lssucceed) {
551       if (++snes->numFailures >= snes->maxFailures) {
552         PetscBool ismin;
553         snes->reason = SNES_DIVERGED_LINE_SEARCH;
554         PetscCall(SNESVICheckLocalMin_Private(snes, snes->jacobian, F, X, gnorm, &ismin));
555         if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
556         break;
557       }
558     }
559     PetscCall(DMDestroyVI(snes->dm));
560     /* Update function and solution vectors */
561     fnorm = gnorm;
562     /* Monitor convergence */
563     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
564     snes->iter  = i + 1;
565     snes->norm  = fnorm;
566     snes->xnorm = xnorm;
567     snes->ynorm = ynorm;
568     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
569     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, lits));
570     /* Test for convergence, xnorm = || X || */
571     if (snes->ops->converged != SNESConvergedSkip) PetscCall(VecNorm(X, NORM_2, &xnorm));
572     PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm));
573     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
574     if (snes->reason) break;
575   }
576   /* 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 */
577   PetscCall(DMDestroyVI(snes->dm));
578   PetscFunctionReturn(PETSC_SUCCESS);
579 }
580 
581 /*@C
582   SNESVISetRedundancyCheck - Provide a function to check for any redundancy in the VI active set
583 
584   Logically Collective
585 
586   Input Parameters:
587 + snes - the `SNESVINEWTONRSLS` context
588 . func - the function to check of redundancies
589 - ctx  - optional context used by the function
590 
591   Level: advanced
592 
593   Note:
594   Sometimes the inactive set will result in a non-singular sub-Jacobian problem that needs to be solved, this allows the user,
595   when they know more about their specific problem to provide a function that removes the redundancy that results in the singular linear system
596 
597 .seealso: `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`, `DMSetVI()`
598  @*/
599 PetscErrorCode SNESVISetRedundancyCheck(SNES snes, PetscErrorCode (*func)(SNES, IS, IS *, void *), void *ctx)
600 {
601   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
602 
603   PetscFunctionBegin;
604   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
605   vi->checkredundancy = func;
606   vi->ctxP            = ctx;
607   PetscFunctionReturn(PETSC_SUCCESS);
608 }
609 
610 #if defined(PETSC_HAVE_MATLAB)
611   #include <engine.h>
612   #include <mex.h>
613 typedef struct {
614   char    *funcname;
615   mxArray *ctx;
616 } SNESMatlabContext;
617 
618 PetscErrorCode SNESVIRedundancyCheck_Matlab(SNES snes, IS is_act, IS *is_redact, void *ctx)
619 {
620   SNESMatlabContext *sctx = (SNESMatlabContext *)ctx;
621   int                nlhs = 1, nrhs = 5;
622   mxArray           *plhs[1], *prhs[5];
623   long long int      l1 = 0, l2 = 0, ls = 0;
624   PetscInt          *indices = NULL;
625 
626   PetscFunctionBegin;
627   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
628   PetscValidHeaderSpecific(is_act, IS_CLASSID, 2);
629   PetscAssertPointer(is_redact, 3);
630   PetscCheckSameComm(snes, 1, is_act, 2);
631 
632   /* Create IS for reduced active set of size 0, its size and indices will
633    bet set by the Matlab function */
634   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), 0, indices, PETSC_OWN_POINTER, is_redact));
635   /* call Matlab function in ctx */
636   PetscCall(PetscArraycpy(&ls, &snes, 1));
637   PetscCall(PetscArraycpy(&l1, &is_act, 1));
638   PetscCall(PetscArraycpy(&l2, is_redact, 1));
639   prhs[0] = mxCreateDoubleScalar((double)ls);
640   prhs[1] = mxCreateDoubleScalar((double)l1);
641   prhs[2] = mxCreateDoubleScalar((double)l2);
642   prhs[3] = mxCreateString(sctx->funcname);
643   prhs[4] = sctx->ctx;
644   PetscCall(mexCallMATLAB(nlhs, plhs, nrhs, prhs, "PetscSNESVIRedundancyCheckInternal"));
645   PetscCall(mxGetScalar(plhs[0]));
646   mxDestroyArray(prhs[0]);
647   mxDestroyArray(prhs[1]);
648   mxDestroyArray(prhs[2]);
649   mxDestroyArray(prhs[3]);
650   mxDestroyArray(plhs[0]);
651   PetscFunctionReturn(PETSC_SUCCESS);
652 }
653 
654 PetscErrorCode SNESVISetRedundancyCheckMatlab(SNES snes, const char *func, mxArray *ctx)
655 {
656   SNESMatlabContext *sctx;
657 
658   PetscFunctionBegin;
659   /* currently sctx is memory bleed */
660   PetscCall(PetscNew(&sctx));
661   PetscCall(PetscStrallocpy(func, &sctx->funcname));
662   sctx->ctx = mxDuplicateArray(ctx);
663   PetscCall(SNESVISetRedundancyCheck(snes, SNESVIRedundancyCheck_Matlab, sctx));
664   PetscFunctionReturn(PETSC_SUCCESS);
665 }
666 
667 #endif
668 
669 static PetscErrorCode SNESSetUp_VINEWTONRSLS(SNES snes)
670 {
671   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
672   PetscInt          *indices;
673   PetscInt           i, n, rstart, rend;
674   SNESLineSearch     linesearch;
675 
676   PetscFunctionBegin;
677   PetscCall(SNESSetUp_VI(snes));
678 
679   /* Set up previous active index set for the first snes solve
680    vi->IS_inact_prev = 0,1,2,....N */
681 
682   PetscCall(VecGetOwnershipRange(snes->vec_sol, &rstart, &rend));
683   PetscCall(VecGetLocalSize(snes->vec_sol, &n));
684   PetscCall(PetscMalloc1(n, &indices));
685   for (i = 0; i < n; i++) indices[i] = rstart + i;
686   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), n, indices, PETSC_OWN_POINTER, &vi->IS_inact_prev));
687 
688   /* set the line search functions */
689   if (!snes->linesearch) {
690     PetscCall(SNESGetLineSearch(snes, &linesearch));
691     PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT));
692   }
693   PetscFunctionReturn(PETSC_SUCCESS);
694 }
695 
696 static PetscErrorCode SNESReset_VINEWTONRSLS(SNES snes)
697 {
698   SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data;
699 
700   PetscFunctionBegin;
701   PetscCall(SNESReset_VI(snes));
702   PetscCall(ISDestroy(&vi->IS_inact_prev));
703   PetscFunctionReturn(PETSC_SUCCESS);
704 }
705 
706 /*MC
707       SNESVINEWTONRSLS - Reduced space active set solvers for variational inequalities based on Newton's method
708 
709    Options Database Keys:
710 +   -snes_type <vinewtonssls,vinewtonrsls> - a semi-smooth solver, a reduced space active set method
711 -   -snes_vi_monitor - prints the number of active constraints at each iteration.
712 
713    Level: beginner
714 
715    References:
716 .  * - T. S. Munson, and S. Benson. Flexible Complementarity Solvers for Large Scale
717      Applications, Optimization Methods and Software, 21 (2006).
718 
719    Note:
720    At each set of this methods the algorithm produces an inactive set of variables that are constrained to their current values
721    (because changing these values would result in those variables no longer satisfying the inequality constraints)
722    and produces a step direction by solving the linear system arising from the Jacobian with the inactive variables removed. In other
723    words on a reduced space of the solution space. Based on the Newton update it then adjusts the inactive sep for the next iteration.
724 
725 .seealso: `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