xref: /petsc/src/snes/impls/vi/vi.c (revision 2205254efee3a00a594e5e2a3a70f74dcb40bc03)
1 #include <petsc-private/snesimpl.h>  /*I "petscsnes.h" I*/
2 
3 #undef __FUNCT__
4 #define __FUNCT__ "SNESVISetComputeVariableBounds"
5 /*@C
6    SNESVISetComputeVariableBounds - Sets a function  that is called to compute the variable bounds
7 
8    Input parameter
9 +  snes - the SNES context
10 -  compute - computes the bounds
11 
12    Level: advanced
13 
14 .seealso:   SNESVISetVariableBounds()
15 
16 @*/
17 PetscErrorCode SNESVISetComputeVariableBounds(SNES snes, PetscErrorCode (*compute)(SNES,Vec,Vec))
18 {
19   PetscErrorCode ierr,(*f)(SNES,PetscErrorCode (*)(SNES,Vec,Vec));
20 
21   PetscFunctionBegin;
22   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
23   ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESVISetComputeVariableBounds_C",(PetscVoidStarFunction)&f);CHKERRQ(ierr);
24   if (!f) {ierr = SNESSetType(snes,SNESVINEWTONRSLS);CHKERRQ(ierr);}
25   ierr = PetscUseMethod(snes,"SNESVISetComputeVariableBounds_C",(SNES,PetscErrorCode (*)(SNES,Vec,Vec)),(snes,compute));CHKERRQ(ierr);
26   PetscFunctionReturn(0);
27 }
28 
29 EXTERN_C_BEGIN
30 #undef __FUNCT__
31 #define __FUNCT__ "SNESVISetComputeVariableBounds_VI"
32 PetscErrorCode SNESVISetComputeVariableBounds_VI(SNES snes,SNESVIComputeVariableBoundsFunction compute)
33 {
34   PetscFunctionBegin;
35   snes->ops->computevariablebounds = compute;
36   PetscFunctionReturn(0);
37 }
38 EXTERN_C_END
39 
40 #undef __FUNCT__
41 #define __FUNCT__ "SNESVIComputeInactiveSetIS"
42 /*
43    SNESVIComputeInactiveSetIS - Gets the global indices for the bogus inactive set variables
44 
45    Input parameter
46 .  snes - the SNES context
47 .  X    - the snes solution vector
48 
49    Output parameter
50 .  ISact - active set index set
51 
52  */
53 PetscErrorCode SNESVIComputeInactiveSetIS(Vec upper,Vec lower,Vec X,Vec F,IS *inact)
54 {
55   PetscErrorCode    ierr;
56   const PetscScalar *x,*xl,*xu,*f;
57   PetscInt          *idx_act,i,nlocal,nloc_isact=0,ilow,ihigh,i1=0;
58 
59   PetscFunctionBegin;
60   ierr = VecGetLocalSize(X,&nlocal);CHKERRQ(ierr);
61   ierr = VecGetOwnershipRange(X,&ilow,&ihigh);CHKERRQ(ierr);
62   ierr = VecGetArrayRead(X,&x);CHKERRQ(ierr);
63   ierr = VecGetArrayRead(lower,&xl);CHKERRQ(ierr);
64   ierr = VecGetArrayRead(upper,&xu);CHKERRQ(ierr);
65   ierr = VecGetArrayRead(F,&f);CHKERRQ(ierr);
66   /* Compute inactive set size */
67   for (i=0; i < nlocal; i++) {
68     if (((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 || (PetscRealPart(f[i]) < 0.0)) && ((PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8) || PetscRealPart(f[i]) > 0.0))) nloc_isact++;
69   }
70 
71   ierr = PetscMalloc(nloc_isact*sizeof(PetscInt),&idx_act);CHKERRQ(ierr);
72 
73   /* Set inactive set indices */
74   for (i=0; i < nlocal; i++) {
75     if (((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 || (PetscRealPart(f[i]) < 0.0)) && ((PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8) || PetscRealPart(f[i]) > 0.0))) idx_act[i1++] = ilow+i;
76   }
77 
78   /* Create inactive set IS */
79   ierr = ISCreateGeneral(((PetscObject)upper)->comm,nloc_isact,idx_act,PETSC_OWN_POINTER,inact);CHKERRQ(ierr);
80 
81   ierr = VecRestoreArrayRead(X,&x);CHKERRQ(ierr);
82   ierr = VecRestoreArrayRead(lower,&xl);CHKERRQ(ierr);
83   ierr = VecRestoreArrayRead(upper,&xu);CHKERRQ(ierr);
84   ierr = VecRestoreArrayRead(F,&f);CHKERRQ(ierr);
85   PetscFunctionReturn(0);
86 }
87 
88 /* --------------------------------------------------------------------------------------------------------*/
89 
90 #undef __FUNCT__
91 #define __FUNCT__ "SNESMonitorVI"
92 PetscErrorCode  SNESMonitorVI(SNES snes,PetscInt its,PetscReal fgnorm,void *dummy)
93 {
94   PetscErrorCode    ierr;
95   PetscViewer       viewer = dummy ? (PetscViewer) dummy : PETSC_VIEWER_STDOUT_(((PetscObject)snes)->comm);
96   const PetscScalar *x,*xl,*xu,*f;
97   PetscInt          i,n,act[2] = {0,0},fact[2],N;
98   /* Number of components that actually hit the bounds (c.f. active variables) */
99   PetscInt  act_bound[2] = {0,0},fact_bound[2];
100   PetscReal rnorm,fnorm;
101   double    tmp;
102 
103   PetscFunctionBegin;
104   ierr = VecGetLocalSize(snes->vec_sol,&n);CHKERRQ(ierr);
105   ierr = VecGetSize(snes->vec_sol,&N);CHKERRQ(ierr);
106   ierr = VecGetArrayRead(snes->xl,&xl);CHKERRQ(ierr);
107   ierr = VecGetArrayRead(snes->xu,&xu);CHKERRQ(ierr);
108   ierr = VecGetArrayRead(snes->vec_sol,&x);CHKERRQ(ierr);
109   ierr = VecGetArrayRead(snes->vec_func,&f);CHKERRQ(ierr);
110 
111   rnorm = 0.0;
112   for (i=0; i<n; i++) {
113     if (((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 || (PetscRealPart(f[i]) < 0.0)) && ((PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8) || PetscRealPart(f[i]) > 0.0))) rnorm += PetscRealPart(PetscConj(f[i])*f[i]);
114     else if (PetscRealPart(x[i]) <= PetscRealPart(xl[i]) + 1.e-8 && PetscRealPart(f[i]) >= 0.0) act[0]++;
115     else if (PetscRealPart(x[i]) >= PetscRealPart(xu[i]) - 1.e-8 && PetscRealPart(f[i]) <= 0.0) act[1]++;
116     else SETERRQ(((PetscObject)snes)->comm,PETSC_ERR_PLIB,"Can never get here");
117   }
118 
119   for (i=0; i<n; i++) {
120     if (PetscRealPart(x[i]) <= PetscRealPart(xl[i]) + 1.e-8) act_bound[0]++;
121     else if (PetscRealPart(x[i]) >= PetscRealPart(xu[i]) - 1.e-8) act_bound[1]++;
122   }
123   ierr  = VecRestoreArrayRead(snes->vec_func,&f);CHKERRQ(ierr);
124   ierr  = VecRestoreArrayRead(snes->xl,&xl);CHKERRQ(ierr);
125   ierr  = VecRestoreArrayRead(snes->xu,&xu);CHKERRQ(ierr);
126   ierr  = VecRestoreArrayRead(snes->vec_sol,&x);CHKERRQ(ierr);
127   ierr  = MPI_Allreduce(&rnorm,&fnorm,1,MPIU_REAL,MPIU_SUM,((PetscObject)snes)->comm);CHKERRQ(ierr);
128   ierr  = MPI_Allreduce(act,fact,2,MPIU_INT,MPI_SUM,((PetscObject)snes)->comm);CHKERRQ(ierr);
129   ierr  = MPI_Allreduce(act_bound,fact_bound,2,MPIU_INT,MPI_SUM,((PetscObject)snes)->comm);CHKERRQ(ierr);
130   fnorm = PetscSqrtReal(fnorm);
131 
132   ierr = PetscViewerASCIIAddTab(viewer,((PetscObject)snes)->tablevel);CHKERRQ(ierr);
133   if (snes->ntruebounds) tmp = ((double)(fact[0]+fact[1]))/((double)snes->ntruebounds);
134   else tmp = 0.0;
135   ierr = PetscViewerASCIIPrintf(viewer,"%3D SNES VI Function norm %14.12e Active lower constraints %D/%D upper constraints %D/%D Percent of total %g Percent of bounded %g\n",its,(double)fnorm,fact[0],fact_bound[0],fact[1],fact_bound[1],((double)(fact[0]+fact[1]))/((double)N),tmp);CHKERRQ(ierr);
136 
137   ierr = PetscViewerASCIISubtractTab(viewer,((PetscObject)snes)->tablevel);CHKERRQ(ierr);
138   PetscFunctionReturn(0);
139 }
140 
141 /*
142      Checks if J^T F = 0 which implies we've found a local minimum of the norm of the function,
143     || F(u) ||_2 but not a zero, F(u) = 0. In the case when one cannot compute J^T F we use the fact that
144     0 = (J^T F)^T W = F^T J W iff W not in the null space of J. Thanks for Jorge More
145     for this trick. One assumes that the probability that W is in the null space of J is very, very small.
146 */
147 #undef __FUNCT__
148 #define __FUNCT__ "SNESVICheckLocalMin_Private"
149 PetscErrorCode SNESVICheckLocalMin_Private(SNES snes,Mat A,Vec F,Vec W,PetscReal fnorm,PetscBool *ismin)
150 {
151   PetscReal      a1;
152   PetscErrorCode ierr;
153   PetscBool      hastranspose;
154 
155   PetscFunctionBegin;
156   *ismin = PETSC_FALSE;
157   ierr   = MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);CHKERRQ(ierr);
158   if (hastranspose) {
159     /* Compute || J^T F|| */
160     ierr = MatMultTranspose(A,F,W);CHKERRQ(ierr);
161     ierr = VecNorm(W,NORM_2,&a1);CHKERRQ(ierr);
162     ierr = PetscInfo1(snes,"|| J^T F|| %g near zero implies found a local minimum\n",(double)(a1/fnorm));CHKERRQ(ierr);
163     if (a1/fnorm < 1.e-4) *ismin = PETSC_TRUE;
164   } else {
165     Vec         work;
166     PetscScalar result;
167     PetscReal   wnorm;
168 
169     ierr = VecSetRandom(W,PETSC_NULL);CHKERRQ(ierr);
170     ierr = VecNorm(W,NORM_2,&wnorm);CHKERRQ(ierr);
171     ierr = VecDuplicate(W,&work);CHKERRQ(ierr);
172     ierr = MatMult(A,W,work);CHKERRQ(ierr);
173     ierr = VecDot(F,work,&result);CHKERRQ(ierr);
174     ierr = VecDestroy(&work);CHKERRQ(ierr);
175     a1   = PetscAbsScalar(result)/(fnorm*wnorm);
176     ierr = PetscInfo1(snes,"(F^T J random)/(|| F ||*||random|| %g near zero implies found a local minimum\n",(double)a1);CHKERRQ(ierr);
177     if (a1 < 1.e-4) *ismin = PETSC_TRUE;
178   }
179   PetscFunctionReturn(0);
180 }
181 
182 /*
183      Checks if J^T(F - J*X) = 0
184 */
185 #undef __FUNCT__
186 #define __FUNCT__ "SNESVICheckResidual_Private"
187 PetscErrorCode SNESVICheckResidual_Private(SNES snes,Mat A,Vec F,Vec X,Vec W1,Vec W2)
188 {
189   PetscReal      a1,a2;
190   PetscErrorCode ierr;
191   PetscBool      hastranspose;
192 
193   PetscFunctionBegin;
194   ierr = MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);CHKERRQ(ierr);
195   if (hastranspose) {
196     ierr = MatMult(A,X,W1);CHKERRQ(ierr);
197     ierr = VecAXPY(W1,-1.0,F);CHKERRQ(ierr);
198 
199     /* Compute || J^T W|| */
200     ierr = MatMultTranspose(A,W1,W2);CHKERRQ(ierr);
201     ierr = VecNorm(W1,NORM_2,&a1);CHKERRQ(ierr);
202     ierr = VecNorm(W2,NORM_2,&a2);CHKERRQ(ierr);
203     if (a1 != 0.0) {
204       ierr = PetscInfo1(snes,"||J^T(F-Ax)||/||F-AX|| %g near zero implies inconsistent rhs\n",(double)(a2/a1));CHKERRQ(ierr);
205     }
206   }
207   PetscFunctionReturn(0);
208 }
209 
210 /*
211   SNESDefaultConverged_VI - Checks the convergence of the semismooth newton algorithm.
212 
213   Notes:
214   The convergence criterion currently implemented is
215   merit < abstol
216   merit < rtol*merit_initial
217 */
218 #undef __FUNCT__
219 #define __FUNCT__ "SNESDefaultConverged_VI"
220 PetscErrorCode SNESDefaultConverged_VI(SNES snes,PetscInt it,PetscReal xnorm,PetscReal gradnorm,PetscReal fnorm,SNESConvergedReason *reason,void *dummy)
221 {
222   PetscErrorCode ierr;
223 
224   PetscFunctionBegin;
225   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
226   PetscValidPointer(reason,6);
227 
228   *reason = SNES_CONVERGED_ITERATING;
229 
230   if (!it) {
231     /* set parameter for default relative tolerance convergence test */
232     snes->ttol = fnorm*snes->rtol;
233   }
234   if (fnorm != fnorm) {
235     ierr    = PetscInfo(snes,"Failed to converged, function norm is NaN\n");CHKERRQ(ierr);
236     *reason = SNES_DIVERGED_FNORM_NAN;
237   } else if (fnorm < snes->abstol) {
238     ierr    = PetscInfo2(snes,"Converged due to function norm %g < %g\n",(double)fnorm,(double)snes->abstol);CHKERRQ(ierr);
239     *reason = SNES_CONVERGED_FNORM_ABS;
240   } else if (snes->nfuncs >= snes->max_funcs) {
241     ierr    = PetscInfo2(snes,"Exceeded maximum number of function evaluations: %D > %D\n",snes->nfuncs,snes->max_funcs);CHKERRQ(ierr);
242     *reason = SNES_DIVERGED_FUNCTION_COUNT;
243   }
244 
245   if (it && !*reason) {
246     if (fnorm < snes->ttol) {
247       ierr    = PetscInfo2(snes,"Converged due to function norm %g < %g (relative tolerance)\n",(double)fnorm,(double)snes->ttol);CHKERRQ(ierr);
248       *reason = SNES_CONVERGED_FNORM_RELATIVE;
249     }
250   }
251   PetscFunctionReturn(0);
252 }
253 
254 
255 /* -------------------------------------------------------------------------- */
256 /*
257    SNESVIProjectOntoBounds - Projects X onto the feasible region so that Xl[i] <= X[i] <= Xu[i] for i = 1...n.
258 
259    Input Parameters:
260 .  SNES - nonlinear solver context
261 
262    Output Parameters:
263 .  X - Bound projected X
264 
265 */
266 
267 #undef __FUNCT__
268 #define __FUNCT__ "SNESVIProjectOntoBounds"
269 PetscErrorCode SNESVIProjectOntoBounds(SNES snes,Vec X)
270 {
271   PetscErrorCode    ierr;
272   const PetscScalar *xl,*xu;
273   PetscScalar       *x;
274   PetscInt          i,n;
275 
276   PetscFunctionBegin;
277   ierr = VecGetLocalSize(X,&n);CHKERRQ(ierr);
278   ierr = VecGetArray(X,&x);CHKERRQ(ierr);
279   ierr = VecGetArrayRead(snes->xl,&xl);CHKERRQ(ierr);
280   ierr = VecGetArrayRead(snes->xu,&xu);CHKERRQ(ierr);
281 
282   for (i = 0; i<n; i++) {
283     if (PetscRealPart(x[i]) < PetscRealPart(xl[i])) x[i] = xl[i];
284     else if (PetscRealPart(x[i]) > PetscRealPart(xu[i])) x[i] = xu[i];
285   }
286   ierr = VecRestoreArray(X,&x);CHKERRQ(ierr);
287   ierr = VecRestoreArrayRead(snes->xl,&xl);CHKERRQ(ierr);
288   ierr = VecRestoreArrayRead(snes->xu,&xu);CHKERRQ(ierr);
289   PetscFunctionReturn(0);
290 }
291 
292 
293 #undef __FUNCT__
294 #define __FUNCT__ "SNESVIGetActiveSetIS"
295 /*
296    SNESVIGetActiveSetIndices - Gets the global indices for the active set variables
297 
298    Input parameter
299 .  snes - the SNES context
300 .  X    - the snes solution vector
301 .  F    - the nonlinear function vector
302 
303    Output parameter
304 .  ISact - active set index set
305  */
306 PetscErrorCode SNESVIGetActiveSetIS(SNES snes,Vec X,Vec F,IS *ISact)
307 {
308   PetscErrorCode    ierr;
309   Vec               Xl=snes->xl,Xu=snes->xu;
310   const PetscScalar *x,*f,*xl,*xu;
311   PetscInt          *idx_act,i,nlocal,nloc_isact=0,ilow,ihigh,i1=0;
312 
313   PetscFunctionBegin;
314   ierr = VecGetLocalSize(X,&nlocal);CHKERRQ(ierr);
315   ierr = VecGetOwnershipRange(X,&ilow,&ihigh);CHKERRQ(ierr);
316   ierr = VecGetArrayRead(X,&x);CHKERRQ(ierr);
317   ierr = VecGetArrayRead(Xl,&xl);CHKERRQ(ierr);
318   ierr = VecGetArrayRead(Xu,&xu);CHKERRQ(ierr);
319   ierr = VecGetArrayRead(F,&f);CHKERRQ(ierr);
320   /* Compute active set size */
321   for (i=0; i < nlocal;i++) {
322     if (!((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 || (PetscRealPart(f[i]) < 0.0)) && ((PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8) || PetscRealPart(f[i]) > 0.0))) nloc_isact++;
323   }
324 
325   ierr = PetscMalloc(nloc_isact*sizeof(PetscInt),&idx_act);CHKERRQ(ierr);
326 
327   /* Set active set indices */
328   for (i=0; i < nlocal; i++) {
329     if (!((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 || (PetscRealPart(f[i]) < 0.0)) && ((PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8) || PetscRealPart(f[i]) > 0.0))) idx_act[i1++] = ilow+i;
330   }
331 
332   /* Create active set IS */
333   ierr = ISCreateGeneral(((PetscObject)snes)->comm,nloc_isact,idx_act,PETSC_OWN_POINTER,ISact);CHKERRQ(ierr);
334 
335   ierr = VecRestoreArrayRead(X,&x);CHKERRQ(ierr);
336   ierr = VecRestoreArrayRead(Xl,&xl);CHKERRQ(ierr);
337   ierr = VecRestoreArrayRead(Xu,&xu);CHKERRQ(ierr);
338   ierr = VecRestoreArrayRead(F,&f);CHKERRQ(ierr);
339   PetscFunctionReturn(0);
340 }
341 
342 #undef __FUNCT__
343 #define __FUNCT__ "SNESVICreateIndexSets_RS"
344 PetscErrorCode SNESVICreateIndexSets_RS(SNES snes,Vec X,Vec F,IS *ISact,IS *ISinact)
345 {
346   PetscErrorCode ierr;
347 
348   PetscFunctionBegin;
349   ierr = SNESVIGetActiveSetIS(snes,X,F,ISact);CHKERRQ(ierr);
350   ierr = ISComplement(*ISact,X->map->rstart,X->map->rend,ISinact);CHKERRQ(ierr);
351   PetscFunctionReturn(0);
352 }
353 
354 #undef __FUNCT__
355 #define __FUNCT__ "SNESVIComputeInactiveSetFnorm"
356 PetscErrorCode SNESVIComputeInactiveSetFnorm(SNES snes,Vec F,Vec X, PetscReal *fnorm)
357 {
358   PetscErrorCode    ierr;
359   const PetscScalar *x,*xl,*xu,*f;
360   PetscInt          i,n;
361   PetscReal         rnorm;
362 
363   PetscFunctionBegin;
364   ierr  = VecGetLocalSize(X,&n);CHKERRQ(ierr);
365   ierr  = VecGetArrayRead(snes->xl,&xl);CHKERRQ(ierr);
366   ierr  = VecGetArrayRead(snes->xu,&xu);CHKERRQ(ierr);
367   ierr  = VecGetArrayRead(X,&x);CHKERRQ(ierr);
368   ierr  = VecGetArrayRead(F,&f);CHKERRQ(ierr);
369   rnorm = 0.0;
370   for (i=0; i<n; i++) {
371     if (((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 || (PetscRealPart(f[i]) < 0.0)) && ((PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8) || PetscRealPart(f[i]) > 0.0))) rnorm += PetscRealPart(PetscConj(f[i])*f[i]);
372   }
373   ierr   = VecRestoreArrayRead(F,&f);CHKERRQ(ierr);
374   ierr   = VecRestoreArrayRead(snes->xl,&xl);CHKERRQ(ierr);
375   ierr   = VecRestoreArrayRead(snes->xu,&xu);CHKERRQ(ierr);
376   ierr   = VecRestoreArrayRead(X,&x);CHKERRQ(ierr);
377   ierr   = MPI_Allreduce(&rnorm,fnorm,1,MPIU_REAL,MPIU_SUM,((PetscObject)snes)->comm);CHKERRQ(ierr);
378   *fnorm = PetscSqrtReal(*fnorm);
379   PetscFunctionReturn(0);
380 }
381 
382 #undef __FUNCT__
383 #define __FUNCT__ "SNESVIDMComputeVariableBounds"
384 PetscErrorCode SNESVIDMComputeVariableBounds(SNES snes,Vec xl, Vec xu)
385 {
386   PetscErrorCode ierr;
387 
388   PetscFunctionBegin;
389   ierr = DMComputeVariableBounds(snes->dm, xl, xu);CHKERRQ(ierr);
390   PetscFunctionReturn(0);
391 }
392 
393 
394 /* -------------------------------------------------------------------------- */
395 /*
396    SNESSetUp_VI - Does setup common to all VI solvers -- basically makes sure bounds have been properly set up
397    of the SNESVI nonlinear solver.
398 
399    Input Parameter:
400 .  snes - the SNES context
401 .  x - the solution vector
402 
403    Application Interface Routine: SNESSetUp()
404 
405    Notes:
406    For basic use of the SNES solvers, the user need not explicitly call
407    SNESSetUp(), since these actions will automatically occur during
408    the call to SNESSolve().
409  */
410 #undef __FUNCT__
411 #define __FUNCT__ "SNESSetUp_VI"
412 PetscErrorCode SNESSetUp_VI(SNES snes)
413 {
414   PetscErrorCode ierr;
415   PetscInt       i_start[3],i_end[3];
416 
417   PetscFunctionBegin;
418   ierr = SNESDefaultGetWork(snes,1);CHKERRQ(ierr);
419   ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
420 
421   if (!snes->ops->computevariablebounds && snes->dm) {
422     PetscBool flag;
423     ierr = DMHasVariableBounds(snes->dm, &flag);CHKERRQ(ierr);
424 
425     snes->ops->computevariablebounds = SNESVIDMComputeVariableBounds;
426   }
427   if (!snes->usersetbounds) {
428     if (snes->ops->computevariablebounds) {
429       if (!snes->xl) {ierr = VecDuplicate(snes->vec_sol,&snes->xl);CHKERRQ(ierr);}
430       if (!snes->xu) {ierr = VecDuplicate(snes->vec_sol,&snes->xu);CHKERRQ(ierr);}
431       ierr = (*snes->ops->computevariablebounds)(snes,snes->xl,snes->xu);CHKERRQ(ierr);
432     } else if (!snes->xl && !snes->xu) {
433       /* If the lower and upper bound on variables are not set, set it to -Inf and Inf */
434       ierr = VecDuplicate(snes->vec_sol, &snes->xl);CHKERRQ(ierr);
435       ierr = VecSet(snes->xl,SNES_VI_NINF);CHKERRQ(ierr);
436       ierr = VecDuplicate(snes->vec_sol, &snes->xu);CHKERRQ(ierr);
437       ierr = VecSet(snes->xu,SNES_VI_INF);CHKERRQ(ierr);
438     } else {
439       /* Check if lower bound, upper bound and solution vector distribution across the processors is identical */
440       ierr = VecGetOwnershipRange(snes->vec_sol,i_start,i_end);CHKERRQ(ierr);
441       ierr = VecGetOwnershipRange(snes->xl,i_start+1,i_end+1);CHKERRQ(ierr);
442       ierr = VecGetOwnershipRange(snes->xu,i_start+2,i_end+2);CHKERRQ(ierr);
443       if ((i_start[0] != i_start[1]) || (i_start[0] != i_start[2]) || (i_end[0] != i_end[1]) || (i_end[0] != i_end[2]))
444         SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Distribution of lower bound, upper bound and the solution vector should be identical across all the processors.");
445     }
446   }
447   PetscFunctionReturn(0);
448 }
449 /* -------------------------------------------------------------------------- */
450 #undef __FUNCT__
451 #define __FUNCT__ "SNESReset_VI"
452 PetscErrorCode SNESReset_VI(SNES snes)
453 {
454   PetscErrorCode ierr;
455 
456   PetscFunctionBegin;
457   ierr                = VecDestroy(&snes->xl);CHKERRQ(ierr);
458   ierr                = VecDestroy(&snes->xu);CHKERRQ(ierr);
459   snes->usersetbounds = PETSC_FALSE;
460   PetscFunctionReturn(0);
461 }
462 
463 /*
464    SNESDestroy_VI - Destroys the private SNES_VI context that was created
465    with SNESCreate_VI().
466 
467    Input Parameter:
468 .  snes - the SNES context
469 
470    Application Interface Routine: SNESDestroy()
471  */
472 #undef __FUNCT__
473 #define __FUNCT__ "SNESDestroy_VI"
474 PetscErrorCode SNESDestroy_VI(SNES snes)
475 {
476   PetscErrorCode ierr;
477 
478   PetscFunctionBegin;
479   ierr = PetscFree(snes->data);CHKERRQ(ierr);
480 
481   /* clear composed functions */
482   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","",PETSC_NULL);CHKERRQ(ierr);
483   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetMonitor_C","",PETSC_NULL);CHKERRQ(ierr);
484   PetscFunctionReturn(0);
485 }
486 
487 #undef __FUNCT__
488 #define __FUNCT__ "SNESVISetVariableBounds"
489 /*@
490    SNESVISetVariableBounds - Sets the lower and upper bounds for the solution vector. xl <= x <= xu.
491 
492    Input Parameters:
493 .  snes - the SNES context.
494 .  xl   - lower bound.
495 .  xu   - upper bound.
496 
497    Notes:
498    If this routine is not called then the lower and upper bounds are set to
499    SNES_VI_INF and SNES_VI_NINF respectively during SNESSetUp().
500 
501    Level: advanced
502 
503 @*/
504 PetscErrorCode SNESVISetVariableBounds(SNES snes, Vec xl, Vec xu)
505 {
506   PetscErrorCode ierr,(*f)(SNES,Vec,Vec);
507 
508   PetscFunctionBegin;
509   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
510   PetscValidHeaderSpecific(xl,VEC_CLASSID,2);
511   PetscValidHeaderSpecific(xu,VEC_CLASSID,3);
512   ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESVISetVariableBounds_C",(PetscVoidStarFunction)&f);CHKERRQ(ierr);
513   if (!f) {ierr = SNESSetType(snes,SNESVINEWTONRSLS);CHKERRQ(ierr);}
514   ierr                = PetscUseMethod(snes,"SNESVISetVariableBounds_C",(SNES,Vec,Vec),(snes,xl,xu));CHKERRQ(ierr);
515   snes->usersetbounds = PETSC_TRUE;
516   PetscFunctionReturn(0);
517 }
518 
519 EXTERN_C_BEGIN
520 #undef __FUNCT__
521 #define __FUNCT__ "SNESVISetVariableBounds_VI"
522 PetscErrorCode SNESVISetVariableBounds_VI(SNES snes,Vec xl,Vec xu)
523 {
524   PetscErrorCode    ierr;
525   const PetscScalar *xxl,*xxu;
526   PetscInt          i,n, cnt = 0;
527 
528   PetscFunctionBegin;
529   ierr = SNESGetFunction(snes,&snes->vec_func,PETSC_NULL,PETSC_NULL);CHKERRQ(ierr);
530   if (!snes->vec_func) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Must call SNESSetFunction() or SNESSetDM() first");
531   if (xl->map->N != snes->vec_func->map->N) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Incompatible vector lengths lower bound = %D solution vector = %D",xl->map->N,snes->vec_func->map->N);
532   if (xu->map->N != snes->vec_func->map->N) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Incompatible vector lengths: upper bound = %D solution vector = %D",xu->map->N,snes->vec_func->map->N);
533   ierr     = SNESSetType(snes,SNESVINEWTONRSLS);CHKERRQ(ierr);
534   ierr     = PetscObjectReference((PetscObject)xl);CHKERRQ(ierr);
535   ierr     = PetscObjectReference((PetscObject)xu);CHKERRQ(ierr);
536   ierr     = VecDestroy(&snes->xl);CHKERRQ(ierr);
537   ierr     = VecDestroy(&snes->xu);CHKERRQ(ierr);
538   snes->xl = xl;
539   snes->xu = xu;
540   ierr     = VecGetLocalSize(xl,&n);CHKERRQ(ierr);
541   ierr     = VecGetArrayRead(xl,&xxl);CHKERRQ(ierr);
542   ierr     = VecGetArrayRead(xu,&xxu);CHKERRQ(ierr);
543   for (i=0; i<n; i++) cnt += ((xxl[i] != SNES_VI_NINF) || (xxu[i] != SNES_VI_INF));
544 
545   ierr = MPI_Allreduce(&cnt,&snes->ntruebounds,1,MPIU_INT,MPI_SUM,((PetscObject)snes)->comm);CHKERRQ(ierr);
546   ierr = VecRestoreArrayRead(xl,&xxl);CHKERRQ(ierr);
547   ierr = VecRestoreArrayRead(xu,&xxu);CHKERRQ(ierr);
548   PetscFunctionReturn(0);
549 }
550 EXTERN_C_END
551 
552 #undef __FUNCT__
553 #define __FUNCT__ "SNESSetFromOptions_VI"
554 PetscErrorCode SNESSetFromOptions_VI(SNES snes)
555 {
556   PetscErrorCode ierr;
557   PetscBool      flg;
558   SNESLineSearch linesearch;
559 
560   PetscFunctionBegin;
561   ierr = PetscOptionsHead("SNES VI options");CHKERRQ(ierr);
562   ierr = PetscOptionsBool("-snes_vi_monitor","Monitor all non-active variables","None",PETSC_FALSE,&flg,0);CHKERRQ(ierr);
563   if (flg) {
564     ierr = SNESMonitorSet(snes,SNESMonitorVI,0,0);CHKERRQ(ierr);
565   }
566   if (!snes->linesearch) {
567     ierr = SNESGetSNESLineSearch(snes, &linesearch);CHKERRQ(ierr);
568     ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHBT);CHKERRQ(ierr);
569     ierr = SNESLineSearchBTSetAlpha(linesearch, 0.0);CHKERRQ(ierr);
570   }
571   ierr = PetscOptionsTail();CHKERRQ(ierr);
572   PetscFunctionReturn(0);
573 }
574