xref: /petsc/src/snes/impls/vi/vi.c (revision ce94432eddcd14845bc7e8083b7f8ea723b9bf7d)
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(PetscObjectComm((PetscObject)upper),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_(PetscObjectComm((PetscObject)snes));
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(PetscObjectComm((PetscObject)snes),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,PetscObjectComm((PetscObject)snes));CHKERRQ(ierr);
128   ierr  = MPI_Allreduce(act,fact,2,MPIU_INT,MPI_SUM,PetscObjectComm((PetscObject)snes));CHKERRQ(ierr);
129   ierr  = MPI_Allreduce(act_bound,fact_bound,2,MPIU_INT,MPI_SUM,PetscObjectComm((PetscObject)snes));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,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(PetscObjectComm((PetscObject)snes),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   PetscInt       rstart,rend;
348 
349   PetscFunctionBegin;
350   ierr = SNESVIGetActiveSetIS(snes,X,F,ISact);CHKERRQ(ierr);
351   ierr = VecGetOwnershipRange(X,&rstart,&rend);CHKERRQ(ierr);
352   ierr = ISComplement(*ISact,rstart,rend,ISinact);CHKERRQ(ierr);
353   PetscFunctionReturn(0);
354 }
355 
356 #undef __FUNCT__
357 #define __FUNCT__ "SNESVIComputeInactiveSetFnorm"
358 PetscErrorCode SNESVIComputeInactiveSetFnorm(SNES snes,Vec F,Vec X, PetscReal *fnorm)
359 {
360   PetscErrorCode    ierr;
361   const PetscScalar *x,*xl,*xu,*f;
362   PetscInt          i,n;
363   PetscReal         rnorm;
364 
365   PetscFunctionBegin;
366   ierr  = VecGetLocalSize(X,&n);CHKERRQ(ierr);
367   ierr  = VecGetArrayRead(snes->xl,&xl);CHKERRQ(ierr);
368   ierr  = VecGetArrayRead(snes->xu,&xu);CHKERRQ(ierr);
369   ierr  = VecGetArrayRead(X,&x);CHKERRQ(ierr);
370   ierr  = VecGetArrayRead(F,&f);CHKERRQ(ierr);
371   rnorm = 0.0;
372   for (i=0; i<n; i++) {
373     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]);
374   }
375   ierr   = VecRestoreArrayRead(F,&f);CHKERRQ(ierr);
376   ierr   = VecRestoreArrayRead(snes->xl,&xl);CHKERRQ(ierr);
377   ierr   = VecRestoreArrayRead(snes->xu,&xu);CHKERRQ(ierr);
378   ierr   = VecRestoreArrayRead(X,&x);CHKERRQ(ierr);
379   ierr   = MPI_Allreduce(&rnorm,fnorm,1,MPIU_REAL,MPIU_SUM,PetscObjectComm((PetscObject)snes));CHKERRQ(ierr);
380   *fnorm = PetscSqrtReal(*fnorm);
381   PetscFunctionReturn(0);
382 }
383 
384 #undef __FUNCT__
385 #define __FUNCT__ "SNESVIDMComputeVariableBounds"
386 PetscErrorCode SNESVIDMComputeVariableBounds(SNES snes,Vec xl, Vec xu)
387 {
388   PetscErrorCode ierr;
389 
390   PetscFunctionBegin;
391   ierr = DMComputeVariableBounds(snes->dm, xl, xu);CHKERRQ(ierr);
392   PetscFunctionReturn(0);
393 }
394 
395 
396 /* -------------------------------------------------------------------------- */
397 /*
398    SNESSetUp_VI - Does setup common to all VI solvers -- basically makes sure bounds have been properly set up
399    of the SNESVI nonlinear solver.
400 
401    Input Parameter:
402 .  snes - the SNES context
403 .  x - the solution vector
404 
405    Application Interface Routine: SNESSetUp()
406 
407    Notes:
408    For basic use of the SNES solvers, the user need not explicitly call
409    SNESSetUp(), since these actions will automatically occur during
410    the call to SNESSolve().
411  */
412 #undef __FUNCT__
413 #define __FUNCT__ "SNESSetUp_VI"
414 PetscErrorCode SNESSetUp_VI(SNES snes)
415 {
416   PetscErrorCode ierr;
417   PetscInt       i_start[3],i_end[3];
418 
419   PetscFunctionBegin;
420   ierr = SNESDefaultGetWork(snes,1);CHKERRQ(ierr);
421   ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
422 
423   if (!snes->ops->computevariablebounds && snes->dm) {
424     PetscBool flag;
425     ierr = DMHasVariableBounds(snes->dm, &flag);CHKERRQ(ierr);
426 
427     snes->ops->computevariablebounds = SNESVIDMComputeVariableBounds;
428   }
429   if (!snes->usersetbounds) {
430     if (snes->ops->computevariablebounds) {
431       if (!snes->xl) {ierr = VecDuplicate(snes->vec_sol,&snes->xl);CHKERRQ(ierr);}
432       if (!snes->xu) {ierr = VecDuplicate(snes->vec_sol,&snes->xu);CHKERRQ(ierr);}
433       ierr = (*snes->ops->computevariablebounds)(snes,snes->xl,snes->xu);CHKERRQ(ierr);
434     } else if (!snes->xl && !snes->xu) {
435       /* If the lower and upper bound on variables are not set, set it to -Inf and Inf */
436       ierr = VecDuplicate(snes->vec_sol, &snes->xl);CHKERRQ(ierr);
437       ierr = VecSet(snes->xl,SNES_VI_NINF);CHKERRQ(ierr);
438       ierr = VecDuplicate(snes->vec_sol, &snes->xu);CHKERRQ(ierr);
439       ierr = VecSet(snes->xu,SNES_VI_INF);CHKERRQ(ierr);
440     } else {
441       /* Check if lower bound, upper bound and solution vector distribution across the processors is identical */
442       ierr = VecGetOwnershipRange(snes->vec_sol,i_start,i_end);CHKERRQ(ierr);
443       ierr = VecGetOwnershipRange(snes->xl,i_start+1,i_end+1);CHKERRQ(ierr);
444       ierr = VecGetOwnershipRange(snes->xu,i_start+2,i_end+2);CHKERRQ(ierr);
445       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]))
446         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.");
447     }
448   }
449   PetscFunctionReturn(0);
450 }
451 /* -------------------------------------------------------------------------- */
452 #undef __FUNCT__
453 #define __FUNCT__ "SNESReset_VI"
454 PetscErrorCode SNESReset_VI(SNES snes)
455 {
456   PetscErrorCode ierr;
457 
458   PetscFunctionBegin;
459   ierr                = VecDestroy(&snes->xl);CHKERRQ(ierr);
460   ierr                = VecDestroy(&snes->xu);CHKERRQ(ierr);
461   snes->usersetbounds = PETSC_FALSE;
462   PetscFunctionReturn(0);
463 }
464 
465 /*
466    SNESDestroy_VI - Destroys the private SNES_VI context that was created
467    with SNESCreate_VI().
468 
469    Input Parameter:
470 .  snes - the SNES context
471 
472    Application Interface Routine: SNESDestroy()
473  */
474 #undef __FUNCT__
475 #define __FUNCT__ "SNESDestroy_VI"
476 PetscErrorCode SNESDestroy_VI(SNES snes)
477 {
478   PetscErrorCode ierr;
479 
480   PetscFunctionBegin;
481   ierr = PetscFree(snes->data);CHKERRQ(ierr);
482 
483   /* clear composed functions */
484   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","",NULL);CHKERRQ(ierr);
485   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetMonitor_C","",NULL);CHKERRQ(ierr);
486   PetscFunctionReturn(0);
487 }
488 
489 #undef __FUNCT__
490 #define __FUNCT__ "SNESVISetVariableBounds"
491 /*@
492    SNESVISetVariableBounds - Sets the lower and upper bounds for the solution vector. xl <= x <= xu.
493 
494    Input Parameters:
495 .  snes - the SNES context.
496 .  xl   - lower bound.
497 .  xu   - upper bound.
498 
499    Notes:
500    If this routine is not called then the lower and upper bounds are set to
501    SNES_VI_INF and SNES_VI_NINF respectively during SNESSetUp().
502 
503    Level: advanced
504 
505 @*/
506 PetscErrorCode SNESVISetVariableBounds(SNES snes, Vec xl, Vec xu)
507 {
508   PetscErrorCode ierr,(*f)(SNES,Vec,Vec);
509 
510   PetscFunctionBegin;
511   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
512   PetscValidHeaderSpecific(xl,VEC_CLASSID,2);
513   PetscValidHeaderSpecific(xu,VEC_CLASSID,3);
514   ierr = PetscObjectQueryFunction((PetscObject)snes,"SNESVISetVariableBounds_C",(PetscVoidStarFunction)&f);CHKERRQ(ierr);
515   if (!f) {ierr = SNESSetType(snes,SNESVINEWTONRSLS);CHKERRQ(ierr);}
516   ierr                = PetscUseMethod(snes,"SNESVISetVariableBounds_C",(SNES,Vec,Vec),(snes,xl,xu));CHKERRQ(ierr);
517   snes->usersetbounds = PETSC_TRUE;
518   PetscFunctionReturn(0);
519 }
520 
521 EXTERN_C_BEGIN
522 #undef __FUNCT__
523 #define __FUNCT__ "SNESVISetVariableBounds_VI"
524 PetscErrorCode SNESVISetVariableBounds_VI(SNES snes,Vec xl,Vec xu)
525 {
526   PetscErrorCode    ierr;
527   const PetscScalar *xxl,*xxu;
528   PetscInt          i,n, cnt = 0;
529 
530   PetscFunctionBegin;
531   ierr = SNESGetFunction(snes,&snes->vec_func,NULL,NULL);CHKERRQ(ierr);
532   if (!snes->vec_func) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Must call SNESSetFunction() or SNESSetDM() first");
533   {
534     PetscInt xlN,xuN,N;
535     ierr = VecGetSize(xl,&xlN);CHKERRQ(ierr);
536     ierr = VecGetSize(xu,&xuN);CHKERRQ(ierr);
537     ierr = VecGetSize(snes->vec_func,&N);CHKERRQ(ierr);
538     if (xlN != N) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Incompatible vector lengths lower bound = %D solution vector = %D",xlN,N);
539     if (xuN != N) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Incompatible vector lengths: upper bound = %D solution vector = %D",xuN,N);
540   }
541   ierr     = SNESSetType(snes,SNESVINEWTONRSLS);CHKERRQ(ierr);
542   ierr     = PetscObjectReference((PetscObject)xl);CHKERRQ(ierr);
543   ierr     = PetscObjectReference((PetscObject)xu);CHKERRQ(ierr);
544   ierr     = VecDestroy(&snes->xl);CHKERRQ(ierr);
545   ierr     = VecDestroy(&snes->xu);CHKERRQ(ierr);
546   snes->xl = xl;
547   snes->xu = xu;
548   ierr     = VecGetLocalSize(xl,&n);CHKERRQ(ierr);
549   ierr     = VecGetArrayRead(xl,&xxl);CHKERRQ(ierr);
550   ierr     = VecGetArrayRead(xu,&xxu);CHKERRQ(ierr);
551   for (i=0; i<n; i++) cnt += ((xxl[i] != SNES_VI_NINF) || (xxu[i] != SNES_VI_INF));
552 
553   ierr = MPI_Allreduce(&cnt,&snes->ntruebounds,1,MPIU_INT,MPI_SUM,PetscObjectComm((PetscObject)snes));CHKERRQ(ierr);
554   ierr = VecRestoreArrayRead(xl,&xxl);CHKERRQ(ierr);
555   ierr = VecRestoreArrayRead(xu,&xxu);CHKERRQ(ierr);
556   PetscFunctionReturn(0);
557 }
558 EXTERN_C_END
559 
560 #undef __FUNCT__
561 #define __FUNCT__ "SNESSetFromOptions_VI"
562 PetscErrorCode SNESSetFromOptions_VI(SNES snes)
563 {
564   PetscErrorCode ierr;
565   PetscBool      flg;
566   SNESLineSearch linesearch;
567 
568   PetscFunctionBegin;
569   ierr = PetscOptionsHead("SNES VI options");CHKERRQ(ierr);
570   ierr = PetscOptionsBool("-snes_vi_monitor","Monitor all non-active variables","None",PETSC_FALSE,&flg,0);CHKERRQ(ierr);
571   if (flg) {
572     ierr = SNESMonitorSet(snes,SNESMonitorVI,0,0);CHKERRQ(ierr);
573   }
574   if (!snes->linesearch) {
575     ierr = SNESGetSNESLineSearch(snes, &linesearch);CHKERRQ(ierr);
576     ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHBT);CHKERRQ(ierr);
577     ierr = SNESLineSearchBTSetAlpha(linesearch, 0.0);CHKERRQ(ierr);
578   }
579   ierr = PetscOptionsTail();CHKERRQ(ierr);
580   PetscFunctionReturn(0);
581 }
582