Actual source code: vi.c
2: #include <../src/snes/impls/vi/viimpl.h> /*I "petscsnes.h" I*/
3: #include <../include/private/kspimpl.h>
4: #include <../include/private/matimpl.h>
5: #include <../include/private/dmimpl.h>
9: /*@C
10: SNESVISetComputeVariableBounds - Sets a function that is called to compute the variable bounds
12: Input parameter
13: + snes - the SNES context
14: - compute - computes the bounds
16: Level: advanced
18: @*/
19: PetscErrorCode SNESVISetComputeVariableBounds(SNES snes, PetscErrorCode (*compute)(SNES,Vec,Vec))
20: {
21: PetscErrorCode ierr;
22: SNES_VI *vi;
25: SNESSetType(snes,SNESVI);
26: vi = (SNES_VI*)snes->data;
27: vi->computevariablebounds = compute;
28: return(0);
29: }
30:
34: /*
35: SNESVIComputeInactiveSetIS - Gets the global indices for the bogus inactive set variables
37: Input parameter
38: . snes - the SNES context
39: . X - the snes solution vector
41: Output parameter
42: . ISact - active set index set
44: */
45: PetscErrorCode SNESVIComputeInactiveSetIS(Vec upper,Vec lower,Vec X,Vec F,IS* inact)
46: {
47: PetscErrorCode ierr;
48: const PetscScalar *x,*xl,*xu,*f;
49: PetscInt *idx_act,i,nlocal,nloc_isact=0,ilow,ihigh,i1=0;
50:
52: VecGetLocalSize(X,&nlocal);
53: VecGetOwnershipRange(X,&ilow,&ihigh);
54: VecGetArrayRead(X,&x);
55: VecGetArrayRead(lower,&xl);
56: VecGetArrayRead(upper,&xu);
57: VecGetArrayRead(F,&f);
58: /* Compute inactive set size */
59: for (i=0; i < nlocal;i++) {
60: 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++;
61: }
63: PetscMalloc(nloc_isact*sizeof(PetscInt),&idx_act);
65: /* Set inactive set indices */
66: for(i=0; i < nlocal; i++) {
67: 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;
68: }
70: /* Create inactive set IS */
71: ISCreateGeneral(((PetscObject)upper)->comm,nloc_isact,idx_act,PETSC_OWN_POINTER,inact);
73: VecRestoreArrayRead(X,&x);
74: VecRestoreArrayRead(lower,&xl);
75: VecRestoreArrayRead(upper,&xu);
76: VecRestoreArrayRead(F,&f);
77: return(0);
78: }
80: /*
81: 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
82: defined by the reduced space method.
84: Simple calls the regular DM interpolation and restricts it to operation on the variables not associated with active constraints.
86: <*/
87: typedef struct {
88: PetscInt n; /* size of vectors in the reduced DM space */
89: IS inactive;
90: PetscErrorCode (*getinterpolation)(DM,DM,Mat*,Vec*); /* DM's original routines */
91: PetscErrorCode (*coarsen)(DM, MPI_Comm, DM*);
92: PetscErrorCode (*createglobalvector)(DM,Vec*);
93: DM dm; /* when destroying this object we need to reset the above function into the base DM */
94: } DM_SNESVI;
98: /*
99: DMCreateGlobalVector_SNESVI - Creates global vector of the size of the reduced space
101: */
102: PetscErrorCode DMCreateGlobalVector_SNESVI(DM dm,Vec *vec)
103: {
104: PetscErrorCode ierr;
105: PetscContainer isnes;
106: DM_SNESVI *dmsnesvi;
109: PetscObjectQuery((PetscObject)dm,"VI",(PetscObject *)&isnes);
110: if (!isnes) SETERRQ(((PetscObject)dm)->comm,PETSC_ERR_PLIB,"Composed SNES is missing");
111: PetscContainerGetPointer(isnes,(void**)&dmsnesvi);
112: VecCreateMPI(((PetscObject)dm)->comm,dmsnesvi->n,PETSC_DETERMINE,vec);
113: return(0);
114: }
118: /*
119: DMGetInterpolation_SNESVI - Modifieds the interpolation obtained from the DM by removing all rows and columns associated with active constraints.
121: */
122: PetscErrorCode DMGetInterpolation_SNESVI(DM dm1,DM dm2,Mat *mat,Vec *vec)
123: {
124: PetscErrorCode ierr;
125: PetscContainer isnes;
126: DM_SNESVI *dmsnesvi1,*dmsnesvi2;
127: Mat interp;
130: PetscObjectQuery((PetscObject)dm1,"VI",(PetscObject *)&isnes);
131: if (!isnes) SETERRQ(((PetscObject)dm1)->comm,PETSC_ERR_PLIB,"Composed VI data structure is missing");
132: PetscContainerGetPointer(isnes,(void**)&dmsnesvi1);
133: PetscObjectQuery((PetscObject)dm2,"VI",(PetscObject *)&isnes);
134: if (!isnes) SETERRQ(((PetscObject)dm2)->comm,PETSC_ERR_PLIB,"Composed VI data structure is missing");
135: PetscContainerGetPointer(isnes,(void**)&dmsnesvi2);
136:
137: (*dmsnesvi1->getinterpolation)(dm1,dm2,&interp,PETSC_NULL);
138: MatGetSubMatrix(interp,dmsnesvi2->inactive,dmsnesvi1->inactive,MAT_INITIAL_MATRIX,mat);
139: MatDestroy(&interp);
140: *vec = 0;
141: return(0);
142: }
148: /*
149: DMCoarsen_SNESVI - Computes the regular coarsened DM then computes additional information about its inactive set
151: */
152: PetscErrorCode DMCoarsen_SNESVI(DM dm1,MPI_Comm comm,DM *dm2)
153: {
154: PetscErrorCode ierr;
155: PetscContainer isnes;
156: DM_SNESVI *dmsnesvi1;
157: Vec finemarked,coarsemarked;
158: IS inactive;
159: VecScatter inject;
160: const PetscInt *index;
161: PetscInt n,k,cnt = 0,rstart,*coarseindex;
162: PetscScalar *marked;
165: PetscObjectQuery((PetscObject)dm1,"VI",(PetscObject *)&isnes);
166: if (!isnes) SETERRQ(((PetscObject)dm1)->comm,PETSC_ERR_PLIB,"Composed VI data structure is missing");
167: PetscContainerGetPointer(isnes,(void**)&dmsnesvi1);
168:
169: /* get the original coarsen */
170: (*dmsnesvi1->coarsen)(dm1,comm,dm2);
172: /* not sure why this extra reference is needed, but without the dm2 disappears too early */
173: PetscObjectReference((PetscObject)*dm2);
175: /* need to set back global vectors in order to use the original injection */
176: DMClearGlobalVectors(dm1);
177: dm1->ops->createglobalvector = dmsnesvi1->createglobalvector;
178: DMCreateGlobalVector(dm1,&finemarked);
179: DMCreateGlobalVector(*dm2,&coarsemarked);
181: /*
182: fill finemarked with locations of inactive points
183: */
184: ISGetIndices(dmsnesvi1->inactive,&index);
185: ISGetLocalSize(dmsnesvi1->inactive,&n);
186: VecSet(finemarked,0.0);
187: for (k=0;k<n;k++){
188: VecSetValue(finemarked,index[k],1.0,INSERT_VALUES);
189: }
190: VecAssemblyBegin(finemarked);
191: VecAssemblyEnd(finemarked);
193: DMGetInjection(*dm2,dm1,&inject);
194: VecScatterBegin(inject,finemarked,coarsemarked,INSERT_VALUES,SCATTER_FORWARD);
195: VecScatterEnd(inject,finemarked,coarsemarked,INSERT_VALUES,SCATTER_FORWARD);
196: VecScatterDestroy(&inject);
198: /*
199: create index set list of coarse inactive points from coarsemarked
200: */
201: VecGetLocalSize(coarsemarked,&n);
202: VecGetOwnershipRange(coarsemarked,&rstart,PETSC_NULL);
203: VecGetArray(coarsemarked,&marked);
204: for (k=0; k<n; k++) {
205: if (marked[k] != 0.0) cnt++;
206: }
207: PetscMalloc(cnt*sizeof(PetscInt),&coarseindex);
208: cnt = 0;
209: for (k=0; k<n; k++) {
210: if (marked[k] != 0.0) coarseindex[cnt++] = k + rstart;
211: }
212: VecRestoreArray(coarsemarked,&marked);
213: ISCreateGeneral(PETSC_COMM_WORLD,cnt,coarseindex,PETSC_OWN_POINTER,&inactive);
215: DMClearGlobalVectors(dm1);
216: dm1->ops->createglobalvector = DMCreateGlobalVector_SNESVI;
217: DMSetVI(*dm2,inactive);
219: VecDestroy(&finemarked);
220: VecDestroy(&coarsemarked);
221: ISDestroy(&inactive);
222: return(0);
223: }
227: PetscErrorCode DMDestroy_SNESVI(DM_SNESVI *dmsnesvi)
228: {
230:
232: /* reset the base methods in the DM object that were changed when the DM_SNESVI was reset */
233: dmsnesvi->dm->ops->getinterpolation = dmsnesvi->getinterpolation;
234: dmsnesvi->dm->ops->coarsen = dmsnesvi->coarsen;
235: dmsnesvi->dm->ops->createglobalvector = dmsnesvi->createglobalvector;
236: /* need to clear out this vectors because some of them may not have a reference to the DM
237: but they are counted as having references to the DM in DMDestroy() */
238: DMClearGlobalVectors(dmsnesvi->dm);
240: ISDestroy(&dmsnesvi->inactive);
241: PetscFree(dmsnesvi);
242: return(0);
243: }
247: /*
248: DMSetVI - Marks a DM as associated with a VI problem. This causes the interpolation/restriction operators to
249: be restricted to only those variables NOT associated with active constraints.
251: */
252: PetscErrorCode DMSetVI(DM dm,IS inactive)
253: {
254: PetscErrorCode ierr;
255: PetscContainer isnes;
256: DM_SNESVI *dmsnesvi;
259: if (!dm) return(0);
261: PetscObjectReference((PetscObject)inactive);
263: PetscObjectQuery((PetscObject)dm,"VI",(PetscObject *)&isnes);
264: if (!isnes) {
265: PetscContainerCreate(((PetscObject)dm)->comm,&isnes);
266: PetscContainerSetUserDestroy(isnes,(PetscErrorCode (*)(void*))DMDestroy_SNESVI);
267: PetscNew(DM_SNESVI,&dmsnesvi);
268: PetscContainerSetPointer(isnes,(void*)dmsnesvi);
269: PetscObjectCompose((PetscObject)dm,"VI",(PetscObject)isnes);
270: PetscContainerDestroy(&isnes);
271: dmsnesvi->getinterpolation = dm->ops->getinterpolation;
272: dm->ops->getinterpolation = DMGetInterpolation_SNESVI;
273: dmsnesvi->coarsen = dm->ops->coarsen;
274: dm->ops->coarsen = DMCoarsen_SNESVI;
275: dmsnesvi->createglobalvector = dm->ops->createglobalvector;
276: dm->ops->createglobalvector = DMCreateGlobalVector_SNESVI;
277: } else {
278: PetscContainerGetPointer(isnes,(void**)&dmsnesvi);
279: ISDestroy(&dmsnesvi->inactive);
280: }
281: DMClearGlobalVectors(dm);
282: ISGetLocalSize(inactive,&dmsnesvi->n);
283: dmsnesvi->inactive = inactive;
284: dmsnesvi->dm = dm;
285: return(0);
286: }
290: /*
291: DMDestroyVI - Frees the DM_SNESVI object contained in the DM
292: - also resets the function pointers in the DM for getinterpolation() etc to use the original DM
293: */
294: PetscErrorCode DMDestroyVI(DM dm)
295: {
296: PetscErrorCode ierr;
299: if (!dm) return(0);
300: PetscObjectCompose((PetscObject)dm,"VI",(PetscObject)PETSC_NULL);
301: return(0);
302: }
304: /* --------------------------------------------------------------------------------------------------------*/
308: PetscErrorCode SNESMonitorVI(SNES snes,PetscInt its,PetscReal fgnorm,void *dummy)
309: {
310: PetscErrorCode ierr;
311: SNES_VI *vi = (SNES_VI*)snes->data;
312: PetscViewer viewer = dummy ? (PetscViewer) dummy : PETSC_VIEWER_STDOUT_(((PetscObject)snes)->comm);
313: const PetscScalar *x,*xl,*xu,*f;
314: PetscInt i,n,act[2] = {0,0},fact[2],N;
315: /* remove later */
316: /* Number of components that actually hit the bounds (c.f. active variables) */
317: PetscInt act_bound[2] = {0,0},fact_bound[2];
318: PetscReal rnorm,fnorm;
321: VecGetLocalSize(snes->vec_sol,&n);
322: VecGetSize(snes->vec_sol,&N);
323: VecGetArrayRead(vi->xl,&xl);
324: VecGetArrayRead(vi->xu,&xu);
325: VecGetArrayRead(snes->vec_sol,&x);
326: VecGetArrayRead(snes->vec_func,&f);
327:
328: rnorm = 0.0;
329: for (i=0; i<n; i++) {
330: 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]);
331: else if (PetscRealPart(x[i]) <= PetscRealPart(xl[i]) + 1.e-8 && PetscRealPart(f[i]) >= 0.0) act[0]++;
332: else if (PetscRealPart(x[i]) >= PetscRealPart(xu[i]) - 1.e-8 && PetscRealPart(f[i]) <= 0.0) act[1]++;
333: else SETERRQ(((PetscObject)snes)->comm,PETSC_ERR_PLIB,"Can never get here");
334: }
336: /* Remove later, number of components that actually hit the bounds */
337: for (i=0; i<n; i++) {
338: if (PetscRealPart(x[i]) <= PetscRealPart(xl[i]) + 1.e-8) act_bound[0]++;
339: else if (PetscRealPart(x[i]) >= PetscRealPart(xu[i]) - 1.e-8) act_bound[1]++;
340: }
341: VecRestoreArrayRead(snes->vec_func,&f);
342: VecRestoreArrayRead(vi->xl,&xl);
343: VecRestoreArrayRead(vi->xu,&xu);
344: VecRestoreArrayRead(snes->vec_sol,&x);
345: MPI_Allreduce(&rnorm,&fnorm,1,MPIU_REAL,MPIU_SUM,((PetscObject)snes)->comm);
346: MPI_Allreduce(act,fact,2,MPIU_INT,MPIU_SUM,((PetscObject)snes)->comm);
347: /* remove later */
348: MPI_Allreduce(act_bound,fact_bound,2,MPIU_INT,MPIU_SUM,((PetscObject)snes)->comm);
349: fnorm = PetscSqrtReal(fnorm);
350:
351: PetscViewerASCIIAddTab(viewer,((PetscObject)snes)->tablevel);
352: PetscViewerASCIIPrintf(viewer,"%3D SNES VI Function norm %14.12e Active lower constraints %D upper constraints %D Percent of total %g Percent of bounded %g\n",its,(double)fnorm,fact[0],fact[1],((double)(fact[0]+fact[1]))/((double)N),((double)(fact[0]+fact[1]))/((double)vi->ntruebounds));
353: PetscViewerASCIIPrintf(viewer," lower constraints satisfied %D upper constraints satisfied %D\n",its,fact_bound[0],fact_bound[1]);
354:
355: PetscViewerASCIISubtractTab(viewer,((PetscObject)snes)->tablevel);
356: return(0);
357: }
359: /*
360: Checks if J^T F = 0 which implies we've found a local minimum of the norm of the function,
361: || 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
362: 0 = (J^T F)^T W = F^T J W iff W not in the null space of J. Thanks for Jorge More
363: for this trick. One assumes that the probability that W is in the null space of J is very, very small.
364: */
367: PetscErrorCode SNESVICheckLocalMin_Private(SNES snes,Mat A,Vec F,Vec W,PetscReal fnorm,PetscBool *ismin)
368: {
369: PetscReal a1;
371: PetscBool hastranspose;
374: *ismin = PETSC_FALSE;
375: MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);
376: if (hastranspose) {
377: /* Compute || J^T F|| */
378: MatMultTranspose(A,F,W);
379: VecNorm(W,NORM_2,&a1);
380: PetscInfo1(snes,"|| J^T F|| %G near zero implies found a local minimum\n",a1/fnorm);
381: if (a1/fnorm < 1.e-4) *ismin = PETSC_TRUE;
382: } else {
383: Vec work;
384: PetscScalar result;
385: PetscReal wnorm;
387: VecSetRandom(W,PETSC_NULL);
388: VecNorm(W,NORM_2,&wnorm);
389: VecDuplicate(W,&work);
390: MatMult(A,W,work);
391: VecDot(F,work,&result);
392: VecDestroy(&work);
393: a1 = PetscAbsScalar(result)/(fnorm*wnorm);
394: PetscInfo1(snes,"(F^T J random)/(|| F ||*||random|| %G near zero implies found a local minimum\n",a1);
395: if (a1 < 1.e-4) *ismin = PETSC_TRUE;
396: }
397: return(0);
398: }
400: /*
401: Checks if J^T(F - J*X) = 0
402: */
405: PetscErrorCode SNESVICheckResidual_Private(SNES snes,Mat A,Vec F,Vec X,Vec W1,Vec W2)
406: {
407: PetscReal a1,a2;
409: PetscBool hastranspose;
412: MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);
413: if (hastranspose) {
414: MatMult(A,X,W1);
415: VecAXPY(W1,-1.0,F);
417: /* Compute || J^T W|| */
418: MatMultTranspose(A,W1,W2);
419: VecNorm(W1,NORM_2,&a1);
420: VecNorm(W2,NORM_2,&a2);
421: if (a1 != 0.0) {
422: PetscInfo1(snes,"||J^T(F-Ax)||/||F-AX|| %G near zero implies inconsistent rhs\n",a2/a1);
423: }
424: }
425: return(0);
426: }
428: /*
429: SNESDefaultConverged_VI - Checks the convergence of the semismooth newton algorithm.
431: Notes:
432: The convergence criterion currently implemented is
433: merit < abstol
434: merit < rtol*merit_initial
435: */
438: PetscErrorCode SNESDefaultConverged_VI(SNES snes,PetscInt it,PetscReal xnorm,PetscReal gradnorm,PetscReal fnorm,SNESConvergedReason *reason,void *dummy)
439: {
445:
446: *reason = SNES_CONVERGED_ITERATING;
448: if (!it) {
449: /* set parameter for default relative tolerance convergence test */
450: snes->ttol = fnorm*snes->rtol;
451: }
452: if (fnorm != fnorm) {
453: PetscInfo(snes,"Failed to converged, function norm is NaN\n");
454: *reason = SNES_DIVERGED_FNORM_NAN;
455: } else if (fnorm < snes->abstol) {
456: PetscInfo2(snes,"Converged due to function norm %G < %G\n",fnorm,snes->abstol);
457: *reason = SNES_CONVERGED_FNORM_ABS;
458: } else if (snes->nfuncs >= snes->max_funcs) {
459: PetscInfo2(snes,"Exceeded maximum number of function evaluations: %D > %D\n",snes->nfuncs,snes->max_funcs);
460: *reason = SNES_DIVERGED_FUNCTION_COUNT;
461: }
463: if (it && !*reason) {
464: if (fnorm < snes->ttol) {
465: PetscInfo2(snes,"Converged due to function norm %G < %G (relative tolerance)\n",fnorm,snes->ttol);
466: *reason = SNES_CONVERGED_FNORM_RELATIVE;
467: }
468: }
469: return(0);
470: }
472: /*
473: SNESVIComputeMeritFunction - Evaluates the merit function for the mixed complementarity problem.
475: Input Parameter:
476: . phi - the semismooth function
478: Output Parameter:
479: . merit - the merit function
480: . phinorm - ||phi||
482: Notes:
483: The merit function for the mixed complementarity problem is defined as
484: merit = 0.5*phi^T*phi
485: */
488: static PetscErrorCode SNESVIComputeMeritFunction(Vec phi, PetscReal* merit,PetscReal* phinorm)
489: {
493: VecNormBegin(phi,NORM_2,phinorm);
494: VecNormEnd(phi,NORM_2,phinorm);
496: *merit = 0.5*(*phinorm)*(*phinorm);
497: return(0);
498: }
500: PETSC_STATIC_INLINE PetscScalar Phi(PetscScalar a,PetscScalar b)
501: {
502: return a + b - PetscSqrtScalar(a*a + b*b);
503: }
505: PETSC_STATIC_INLINE PetscScalar DPhi(PetscScalar a,PetscScalar b)
506: {
507: if ((PetscAbsScalar(a) >= 1.e-6) || (PetscAbsScalar(b) >= 1.e-6)) return 1.0 - a/ PetscSqrtScalar(a*a + b*b);
508: else return .5;
509: }
511: /*
512: SNESVIComputeFunction - Reformulates a system of nonlinear equations in mixed complementarity form to a system of nonlinear equations in semismooth form.
514: Input Parameters:
515: . snes - the SNES context
516: . x - current iterate
517: . functx - user defined function context
519: Output Parameters:
520: . phi - Semismooth function
522: */
525: static PetscErrorCode SNESVIComputeFunction(SNES snes,Vec X,Vec phi,void* functx)
526: {
527: PetscErrorCode ierr;
528: SNES_VI *vi = (SNES_VI*)snes->data;
529: Vec Xl = vi->xl,Xu = vi->xu,F = snes->vec_func;
530: PetscScalar *phi_arr,*x_arr,*f_arr,*l,*u;
531: PetscInt i,nlocal;
534: (*vi->computeuserfunction)(snes,X,F,functx);
535: VecGetLocalSize(X,&nlocal);
536: VecGetArray(X,&x_arr);
537: VecGetArray(F,&f_arr);
538: VecGetArray(Xl,&l);
539: VecGetArray(Xu,&u);
540: VecGetArray(phi,&phi_arr);
542: for (i=0;i < nlocal;i++) {
543: if ((PetscRealPart(l[i]) <= SNES_VI_NINF) && (PetscRealPart(u[i]) >= SNES_VI_INF)) { /* no constraints on variable */
544: phi_arr[i] = f_arr[i];
545: } else if (PetscRealPart(l[i]) <= SNES_VI_NINF) { /* upper bound on variable only */
546: phi_arr[i] = -Phi(u[i] - x_arr[i],-f_arr[i]);
547: } else if (PetscRealPart(u[i]) >= SNES_VI_INF) { /* lower bound on variable only */
548: phi_arr[i] = Phi(x_arr[i] - l[i],f_arr[i]);
549: } else if (l[i] == u[i]) {
550: phi_arr[i] = l[i] - x_arr[i];
551: } else { /* both bounds on variable */
552: phi_arr[i] = Phi(x_arr[i] - l[i],-Phi(u[i] - x_arr[i],-f_arr[i]));
553: }
554: }
555:
556: VecRestoreArray(X,&x_arr);
557: VecRestoreArray(F,&f_arr);
558: VecRestoreArray(Xl,&l);
559: VecRestoreArray(Xu,&u);
560: VecRestoreArray(phi,&phi_arr);
561: return(0);
562: }
564: /*
565: SNESVIComputeBsubdifferentialVectors - Computes the diagonal shift (Da) and row scaling (Db) vectors needed for the
566: the semismooth jacobian.
567: */
570: PetscErrorCode SNESVIComputeBsubdifferentialVectors(SNES snes,Vec X,Vec F,Mat jac,Vec Da,Vec Db)
571: {
573: SNES_VI *vi = (SNES_VI*)snes->data;
574: PetscScalar *l,*u,*x,*f,*da,*db,da1,da2,db1,db2;
575: PetscInt i,nlocal;
579: VecGetArray(X,&x);
580: VecGetArray(F,&f);
581: VecGetArray(vi->xl,&l);
582: VecGetArray(vi->xu,&u);
583: VecGetArray(Da,&da);
584: VecGetArray(Db,&db);
585: VecGetLocalSize(X,&nlocal);
586:
587: for (i=0;i< nlocal;i++) {
588: if ((PetscRealPart(l[i]) <= SNES_VI_NINF) && (PetscRealPart(u[i]) >= SNES_VI_INF)) {/* no constraints on variable */
589: da[i] = 0;
590: db[i] = 1;
591: } else if (PetscRealPart(l[i]) <= SNES_VI_NINF) { /* upper bound on variable only */
592: da[i] = DPhi(u[i] - x[i], -f[i]);
593: db[i] = DPhi(-f[i],u[i] - x[i]);
594: } else if (PetscRealPart(u[i]) >= SNES_VI_INF) { /* lower bound on variable only */
595: da[i] = DPhi(x[i] - l[i], f[i]);
596: db[i] = DPhi(f[i],x[i] - l[i]);
597: } else if (l[i] == u[i]) { /* fixed variable */
598: da[i] = 1;
599: db[i] = 0;
600: } else { /* upper and lower bounds on variable */
601: da1 = DPhi(x[i] - l[i], -Phi(u[i] - x[i], -f[i]));
602: db1 = DPhi(-Phi(u[i] - x[i], -f[i]),x[i] - l[i]);
603: da2 = DPhi(u[i] - x[i], -f[i]);
604: db2 = DPhi(-f[i],u[i] - x[i]);
605: da[i] = da1 + db1*da2;
606: db[i] = db1*db2;
607: }
608: }
610: VecRestoreArray(X,&x);
611: VecRestoreArray(F,&f);
612: VecRestoreArray(vi->xl,&l);
613: VecRestoreArray(vi->xu,&u);
614: VecRestoreArray(Da,&da);
615: VecRestoreArray(Db,&db);
616: return(0);
617: }
619: /*
620: SNESVIComputeJacobian - Computes the jacobian of the semismooth function.The Jacobian for the semismooth function is an element of the B-subdifferential of the Fischer-Burmeister function for complementarity problems.
622: Input Parameters:
623: . Da - Diagonal shift vector for the semismooth jacobian.
624: . Db - Row scaling vector for the semismooth jacobian.
626: Output Parameters:
627: . jac - semismooth jacobian
628: . jac_pre - optional preconditioning matrix
630: Notes:
631: The semismooth jacobian matrix is given by
632: jac = Da + Db*jacfun
633: where Db is the row scaling matrix stored as a vector,
634: Da is the diagonal perturbation matrix stored as a vector
635: and jacfun is the jacobian of the original nonlinear function.
636: */
639: PetscErrorCode SNESVIComputeJacobian(Mat jac, Mat jac_pre,Vec Da, Vec Db)
640: {
642:
643: /* Do row scaling and add diagonal perturbation */
644: MatDiagonalScale(jac,Db,PETSC_NULL);
645: MatDiagonalSet(jac,Da,ADD_VALUES);
646: if (jac != jac_pre) { /* If jac and jac_pre are different */
647: MatDiagonalScale(jac_pre,Db,PETSC_NULL);
648: MatDiagonalSet(jac_pre,Da,ADD_VALUES);
649: }
650: return(0);
651: }
653: /*
654: SNESVIComputeMeritFunctionGradient - Computes the gradient of the merit function psi.
656: Input Parameters:
657: phi - semismooth function.
658: H - semismooth jacobian
659:
660: Output Parameters:
661: dpsi - merit function gradient
663: Notes:
664: The merit function gradient is computed as follows
665: dpsi = H^T*phi
666: */
669: PetscErrorCode SNESVIComputeMeritFunctionGradient(Mat H, Vec phi, Vec dpsi)
670: {
672:
674: MatMultTranspose(H,phi,dpsi);
675: return(0);
676: }
678: /* -------------------------------------------------------------------------- */
679: /*
680: SNESVIProjectOntoBounds - Projects X onto the feasible region so that Xl[i] <= X[i] <= Xu[i] for i = 1...n.
682: Input Parameters:
683: . SNES - nonlinear solver context
685: Output Parameters:
686: . X - Bound projected X
688: */
692: PetscErrorCode SNESVIProjectOntoBounds(SNES snes,Vec X)
693: {
694: PetscErrorCode ierr;
695: SNES_VI *vi = (SNES_VI*)snes->data;
696: const PetscScalar *xl,*xu;
697: PetscScalar *x;
698: PetscInt i,n;
701: VecGetLocalSize(X,&n);
702: VecGetArray(X,&x);
703: VecGetArrayRead(vi->xl,&xl);
704: VecGetArrayRead(vi->xu,&xu);
706: for(i = 0;i<n;i++) {
707: if (PetscRealPart(x[i]) < PetscRealPart(xl[i])) x[i] = xl[i];
708: else if (PetscRealPart(x[i]) > PetscRealPart(xu[i])) x[i] = xu[i];
709: }
710: VecRestoreArray(X,&x);
711: VecRestoreArrayRead(vi->xl,&xl);
712: VecRestoreArrayRead(vi->xu,&xu);
713: return(0);
714: }
716: /* --------------------------------------------------------------------
718: This file implements a semismooth truncated Newton method with a line search,
719: for solving a system of nonlinear equations in complementarity form, using the KSP, Vec,
720: and Mat interfaces for linear solvers, vectors, and matrices,
721: respectively.
723: The following basic routines are required for each nonlinear solver:
724: SNESCreate_XXX() - Creates a nonlinear solver context
725: SNESSetFromOptions_XXX() - Sets runtime options
726: SNESSolve_XXX() - Solves the nonlinear system
727: SNESDestroy_XXX() - Destroys the nonlinear solver context
728: The suffix "_XXX" denotes a particular implementation, in this case
729: we use _VI (e.g., SNESCreate_VI, SNESSolve_VI) for solving
730: systems of nonlinear equations with a line search (LS) method.
731: These routines are actually called via the common user interface
732: routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and
733: SNESDestroy(), so the application code interface remains identical
734: for all nonlinear solvers.
736: Another key routine is:
737: SNESSetUp_XXX() - Prepares for the use of a nonlinear solver
738: by setting data structures and options. The interface routine SNESSetUp()
739: is not usually called directly by the user, but instead is called by
740: SNESSolve() if necessary.
742: Additional basic routines are:
743: SNESView_XXX() - Prints details of runtime options that
744: have actually been used.
745: These are called by application codes via the interface routines
746: SNESView().
748: The various types of solvers (preconditioners, Krylov subspace methods,
749: nonlinear solvers, timesteppers) are all organized similarly, so the
750: above description applies to these categories also.
752: -------------------------------------------------------------------- */
753: /*
754: SNESSolveVI_SS - Solves the complementarity problem with a semismooth Newton
755: method using a line search.
757: Input Parameters:
758: . snes - the SNES context
760: Output Parameter:
761: . outits - number of iterations until termination
763: Application Interface Routine: SNESSolve()
765: Notes:
766: This implements essentially a semismooth Newton method with a
767: line search. The default line search does not do any line seach
768: but rather takes a full newton step.
769: */
772: PetscErrorCode SNESSolveVI_SS(SNES snes)
773: {
774: SNES_VI *vi = (SNES_VI*)snes->data;
775: PetscErrorCode ierr;
776: PetscInt maxits,i,lits;
777: PetscBool lssucceed;
778: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
779: PetscReal gnorm,xnorm=0,ynorm;
780: Vec Y,X,F,G,W;
781: KSPConvergedReason kspreason;
784: vi->computeuserfunction = snes->ops->computefunction;
785: snes->ops->computefunction = SNESVIComputeFunction;
787: snes->numFailures = 0;
788: snes->numLinearSolveFailures = 0;
789: snes->reason = SNES_CONVERGED_ITERATING;
791: maxits = snes->max_its; /* maximum number of iterations */
792: X = snes->vec_sol; /* solution vector */
793: F = snes->vec_func; /* residual vector */
794: Y = snes->work[0]; /* work vectors */
795: G = snes->work[1];
796: W = snes->work[2];
798: PetscObjectTakeAccess(snes);
799: snes->iter = 0;
800: snes->norm = 0.0;
801: PetscObjectGrantAccess(snes);
803: SNESVIProjectOntoBounds(snes,X);
804: SNESComputeFunction(snes,X,vi->phi);
805: if (snes->domainerror) {
806: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
807: snes->ops->computefunction = vi->computeuserfunction;
808: return(0);
809: }
810: /* Compute Merit function */
811: SNESVIComputeMeritFunction(vi->phi,&vi->merit,&vi->phinorm);
813: VecNormBegin(X,NORM_2,&xnorm); /* xnorm <- ||x|| */
814: VecNormEnd(X,NORM_2,&xnorm);
815: if (PetscIsInfOrNanReal(vi->merit)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
817: PetscObjectTakeAccess(snes);
818: snes->norm = vi->phinorm;
819: PetscObjectGrantAccess(snes);
820: SNESLogConvHistory(snes,vi->phinorm,0);
821: SNESMonitor(snes,0,vi->phinorm);
823: /* set parameter for default relative tolerance convergence test */
824: snes->ttol = vi->phinorm*snes->rtol;
825: /* test convergence */
826: (*snes->ops->converged)(snes,0,0.0,0.0,vi->phinorm,&snes->reason,snes->cnvP);
827: if (snes->reason) {
828: snes->ops->computefunction = vi->computeuserfunction;
829: return(0);
830: }
832: for (i=0; i<maxits; i++) {
834: /* Call general purpose update function */
835: if (snes->ops->update) {
836: (*snes->ops->update)(snes, snes->iter);
837: }
838:
839: /* Solve J Y = Phi, where J is the semismooth jacobian */
840: /* Get the nonlinear function jacobian */
841: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
842: /* Get the diagonal shift and row scaling vectors */
843: SNESVIComputeBsubdifferentialVectors(snes,X,F,snes->jacobian,vi->Da,vi->Db);
844: /* Compute the semismooth jacobian */
845: SNESVIComputeJacobian(snes->jacobian,snes->jacobian_pre,vi->Da,vi->Db);
846: /* Compute the merit function gradient */
847: SNESVIComputeMeritFunctionGradient(snes->jacobian,vi->phi,vi->dpsi);
848: KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
849: SNES_KSPSolve(snes,snes->ksp,vi->phi,Y);
850: KSPGetConvergedReason(snes->ksp,&kspreason);
852: if (kspreason < 0) {
853: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
854: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
855: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
856: break;
857: }
858: }
859: KSPGetIterationNumber(snes->ksp,&lits);
860: snes->linear_its += lits;
861: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
862: /*
863: if (vi->precheckstep) {
864: PetscBool changed_y = PETSC_FALSE;
865: (*vi->precheckstep)(snes,X,Y,vi->precheck,&changed_y);
866: }
868: if (PetscLogPrintInfo){
869: SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
870: }
871: */
872: /* Compute a (scaled) negative update in the line search routine:
873: Y <- X - lambda*Y
874: and evaluate G = function(Y) (depends on the line search).
875: */
876: VecCopy(Y,snes->vec_sol_update);
877: ynorm = 1; gnorm = vi->phinorm;
878: (*vi->LineSearch)(snes,vi->lsP,X,vi->phi,G,Y,W,vi->phinorm,xnorm,&ynorm,&gnorm,&lssucceed);
879: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",vi->phinorm,gnorm,ynorm,(int)lssucceed);
880: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
881: if (snes->domainerror) {
882: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
883: snes->ops->computefunction = vi->computeuserfunction;
884: return(0);
885: }
886: if (!lssucceed) {
887: if (++snes->numFailures >= snes->maxFailures) {
888: PetscBool ismin;
889: snes->reason = SNES_DIVERGED_LINE_SEARCH;
890: SNESVICheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);
891: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
892: break;
893: }
894: }
895: /* Update function and solution vectors */
896: vi->phinorm = gnorm;
897: vi->merit = 0.5*vi->phinorm*vi->phinorm;
898: VecCopy(G,vi->phi);
899: VecCopy(W,X);
900: /* Monitor convergence */
901: PetscObjectTakeAccess(snes);
902: snes->iter = i+1;
903: snes->norm = vi->phinorm;
904: PetscObjectGrantAccess(snes);
905: SNESLogConvHistory(snes,snes->norm,lits);
906: SNESMonitor(snes,snes->iter,snes->norm);
907: /* Test for convergence, xnorm = || X || */
908: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
909: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,vi->phinorm,&snes->reason,snes->cnvP);
910: if (snes->reason) break;
911: }
912: if (i == maxits) {
913: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
914: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
915: }
916: snes->ops->computefunction = vi->computeuserfunction;
917: return(0);
918: }
922: /*
923: SNESVIGetActiveSetIndices - Gets the global indices for the active set variables
925: Input parameter
926: . snes - the SNES context
927: . X - the snes solution vector
928: . F - the nonlinear function vector
930: Output parameter
931: . ISact - active set index set
932: */
933: PetscErrorCode SNESVIGetActiveSetIS(SNES snes,Vec X,Vec F,IS* ISact)
934: {
935: PetscErrorCode ierr;
936: SNES_VI *vi = (SNES_VI*)snes->data;
937: Vec Xl=vi->xl,Xu=vi->xu;
938: const PetscScalar *x,*f,*xl,*xu;
939: PetscInt *idx_act,i,nlocal,nloc_isact=0,ilow,ihigh,i1=0;
940:
942: VecGetLocalSize(X,&nlocal);
943: VecGetOwnershipRange(X,&ilow,&ihigh);
944: VecGetArrayRead(X,&x);
945: VecGetArrayRead(Xl,&xl);
946: VecGetArrayRead(Xu,&xu);
947: VecGetArrayRead(F,&f);
948: /* Compute active set size */
949: for (i=0; i < nlocal;i++) {
950: if (!vi->ignorefunctionsign) {
951: 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++;
952: } else {
953: if (!(PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 && PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8)) nloc_isact++;
954: }
955: }
957: PetscMalloc(nloc_isact*sizeof(PetscInt),&idx_act);
959: /* Set active set indices */
960: for(i=0; i < nlocal; i++) {
961: if (!vi->ignorefunctionsign) {
962: 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;
963: } else {
964: if (!(PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8 && PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8)) idx_act[i1++] = ilow+i;
965: }
966: }
968: /* Create active set IS */
969: ISCreateGeneral(((PetscObject)snes)->comm,nloc_isact,idx_act,PETSC_OWN_POINTER,ISact);
971: VecRestoreArrayRead(X,&x);
972: VecRestoreArrayRead(Xl,&xl);
973: VecRestoreArrayRead(Xu,&xu);
974: VecRestoreArrayRead(F,&f);
975: return(0);
976: }
980: PetscErrorCode SNESVICreateIndexSets_RS(SNES snes,Vec X,Vec F,IS* ISact,IS* ISinact)
981: {
982: PetscErrorCode ierr;
985: SNESVIGetActiveSetIS(snes,X,F,ISact);
986: ISComplement(*ISact,X->map->rstart,X->map->rend,ISinact);
987: return(0);
988: }
990: /* Create active and inactive set vectors. The local size of this vector is set and petsc computes the global size */
993: PetscErrorCode SNESVICreateSubVectors(SNES snes,PetscInt n,Vec* newv)
994: {
996: Vec v;
999: VecCreate(((PetscObject)snes)->comm,&v);
1000: VecSetSizes(v,n,PETSC_DECIDE);
1001: VecSetFromOptions(v);
1002: *newv = v;
1004: return(0);
1005: }
1007: /* Resets the snes PC and KSP when the active set sizes change */
1010: PetscErrorCode SNESVIResetPCandKSP(SNES snes,Mat Amat,Mat Pmat)
1011: {
1012: PetscErrorCode ierr;
1013: KSP snesksp;
1016: SNESGetKSP(snes,&snesksp);
1017: KSPReset(snesksp);
1019: /*
1020: KSP kspnew;
1021: PC pcnew;
1022: const MatSolverPackage stype;
1025: KSPCreate(((PetscObject)snes)->comm,&kspnew);
1026: kspnew->pc_side = snesksp->pc_side;
1027: kspnew->rtol = snesksp->rtol;
1028: kspnew->abstol = snesksp->abstol;
1029: kspnew->max_it = snesksp->max_it;
1030: KSPSetType(kspnew,((PetscObject)snesksp)->type_name);
1031: KSPGetPC(kspnew,&pcnew);
1032: PCSetType(kspnew->pc,((PetscObject)snesksp->pc)->type_name);
1033: PCSetOperators(kspnew->pc,Amat,Pmat,DIFFERENT_NONZERO_PATTERN);
1034: PCFactorGetMatSolverPackage(snesksp->pc,&stype);
1035: PCFactorSetMatSolverPackage(kspnew->pc,stype);
1036: KSPDestroy(&snesksp);
1037: snes->ksp = kspnew;
1038: PetscLogObjectParent(snes,kspnew);
1039: KSPSetFromOptions(kspnew);*/
1040: return(0);
1041: }
1046: PetscErrorCode SNESVIComputeInactiveSetFnorm(SNES snes,Vec F,Vec X,PetscReal *fnorm)
1047: {
1048: PetscErrorCode ierr;
1049: SNES_VI *vi = (SNES_VI*)snes->data;
1050: const PetscScalar *x,*xl,*xu,*f;
1051: PetscInt i,n;
1052: PetscReal rnorm;
1055: VecGetLocalSize(X,&n);
1056: VecGetArrayRead(vi->xl,&xl);
1057: VecGetArrayRead(vi->xu,&xu);
1058: VecGetArrayRead(X,&x);
1059: VecGetArrayRead(F,&f);
1060: rnorm = 0.0;
1061: if (!vi->ignorefunctionsign) {
1062: for (i=0; i<n; i++) {
1063: 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]);
1064: }
1065: } else {
1066: for (i=0; i<n; i++) {
1067: if ((PetscRealPart(x[i]) > PetscRealPart(xl[i]) + 1.e-8) && (PetscRealPart(x[i]) < PetscRealPart(xu[i]) - 1.e-8)) rnorm += PetscRealPart(PetscConj(f[i])*f[i]);
1068: }
1069: }
1070: VecRestoreArrayRead(F,&f);
1071: VecRestoreArrayRead(vi->xl,&xl);
1072: VecRestoreArrayRead(vi->xu,&xu);
1073: VecRestoreArrayRead(X,&x);
1074: MPI_Allreduce(&rnorm,fnorm,1,MPIU_REAL,MPIU_SUM,((PetscObject)snes)->comm);
1075: *fnorm = sqrt(*fnorm);
1076: return(0);
1077: }
1079: /* Variational Inequality solver using reduce space method. No semismooth algorithm is
1080: implemented in this algorithm. It basically identifies the active constraints and does
1081: a linear solve on the other variables (those not associated with the active constraints). */
1084: PetscErrorCode SNESSolveVI_RS(SNES snes)
1085: {
1086: SNES_VI *vi = (SNES_VI*)snes->data;
1087: PetscErrorCode ierr;
1088: PetscInt maxits,i,lits;
1089: PetscBool lssucceed;
1090: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
1091: PetscReal fnorm,gnorm,xnorm=0,ynorm;
1092: Vec Y,X,F,G,W;
1093: KSPConvergedReason kspreason;
1096: snes->numFailures = 0;
1097: snes->numLinearSolveFailures = 0;
1098: snes->reason = SNES_CONVERGED_ITERATING;
1100: maxits = snes->max_its; /* maximum number of iterations */
1101: X = snes->vec_sol; /* solution vector */
1102: F = snes->vec_func; /* residual vector */
1103: Y = snes->work[0]; /* work vectors */
1104: G = snes->work[1];
1105: W = snes->work[2];
1107: PetscObjectTakeAccess(snes);
1108: snes->iter = 0;
1109: snes->norm = 0.0;
1110: PetscObjectGrantAccess(snes);
1112: SNESVIProjectOntoBounds(snes,X);
1113: SNESComputeFunction(snes,X,F);
1114: if (snes->domainerror) {
1115: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1116: return(0);
1117: }
1118: SNESVIComputeInactiveSetFnorm(snes,F,X,&fnorm);
1119: VecNormBegin(X,NORM_2,&xnorm); /* xnorm <- ||x|| */
1120: VecNormEnd(X,NORM_2,&xnorm);
1121: if (PetscIsInfOrNanReal(fnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
1123: PetscObjectTakeAccess(snes);
1124: snes->norm = fnorm;
1125: PetscObjectGrantAccess(snes);
1126: SNESLogConvHistory(snes,fnorm,0);
1127: SNESMonitor(snes,0,fnorm);
1129: /* set parameter for default relative tolerance convergence test */
1130: snes->ttol = fnorm*snes->rtol;
1131: /* test convergence */
1132: (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
1133: if (snes->reason) return(0);
1136: for (i=0; i<maxits; i++) {
1138: IS IS_act,IS_inact; /* _act -> active set _inact -> inactive set */
1139: IS IS_redact; /* redundant active set */
1140: VecScatter scat_act,scat_inact;
1141: PetscInt nis_act,nis_inact;
1142: Vec Y_act,Y_inact,F_inact;
1143: Mat jac_inact_inact,prejac_inact_inact;
1144: IS keptrows;
1145: PetscBool isequal;
1147: /* Call general purpose update function */
1148: if (snes->ops->update) {
1149: (*snes->ops->update)(snes, snes->iter);
1150: }
1151: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
1154: /* Create active and inactive index sets */
1155:
1156: /*original
1157: SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&IS_inact);
1158: */
1159: SNESVIGetActiveSetIS(snes,X,F,&IS_act);
1160:
1161: if (vi->checkredundancy) {
1162: (*vi->checkredundancy)(snes,IS_act,&IS_redact,vi->ctxP);
1163: if (IS_redact){
1164: ISSort(IS_redact);
1165: ISComplement(IS_redact,X->map->rstart,X->map->rend,&IS_inact);
1166: ISDestroy(&IS_redact);
1167: }
1168: else {
1169: ISComplement(IS_act,X->map->rstart,X->map->rend,&IS_inact);
1170: }
1171: } else {
1172: ISComplement(IS_act,X->map->rstart,X->map->rend,&IS_inact);
1173: }
1174:
1176: /* Create inactive set submatrix */
1177: MatGetSubMatrix(snes->jacobian,IS_inact,IS_inact,MAT_INITIAL_MATRIX,&jac_inact_inact);
1178:
1179: MatFindNonzeroRows(jac_inact_inact,&keptrows);
1180: if (0 && keptrows) {
1181: PetscInt cnt,*nrows,k;
1182: const PetscInt *krows,*inact;
1183: PetscInt rstart=jac_inact_inact->rmap->rstart;
1185: MatDestroy(&jac_inact_inact);
1186: ISDestroy(&IS_act);
1188: ISGetLocalSize(keptrows,&cnt);
1189: ISGetIndices(keptrows,&krows);
1190: ISGetIndices(IS_inact,&inact);
1191: PetscMalloc(cnt*sizeof(PetscInt),&nrows);
1192: for (k=0; k<cnt; k++) {
1193: nrows[k] = inact[krows[k]-rstart];
1194: }
1195: ISRestoreIndices(keptrows,&krows);
1196: ISRestoreIndices(IS_inact,&inact);
1197: ISDestroy(&keptrows);
1198: ISDestroy(&IS_inact);
1199:
1200: ISCreateGeneral(PETSC_COMM_WORLD,cnt,nrows,PETSC_OWN_POINTER,&IS_inact);
1201: ISComplement(IS_inact,F->map->rstart,F->map->rend,&IS_act);
1202: MatGetSubMatrix(snes->jacobian,IS_inact,IS_inact,MAT_INITIAL_MATRIX,&jac_inact_inact);
1203: }
1204: DMSetVI(snes->dm,IS_inact);
1205: /* remove later */
1207: /*
1208: VecView(vi->xu,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1209: VecView(vi->xl,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1210: VecView(X,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1211: VecView(F,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1212: ISView(IS_inact,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1213: */
1215: /* Get sizes of active and inactive sets */
1216: ISGetLocalSize(IS_act,&nis_act);
1217: ISGetLocalSize(IS_inact,&nis_inact);
1219: /* Create active and inactive set vectors */
1220: SNESVICreateSubVectors(snes,nis_inact,&F_inact);
1221: SNESVICreateSubVectors(snes,nis_act,&Y_act);
1222: SNESVICreateSubVectors(snes,nis_inact,&Y_inact);
1224: /* Create scatter contexts */
1225: VecScatterCreate(Y,IS_act,Y_act,PETSC_NULL,&scat_act);
1226: VecScatterCreate(Y,IS_inact,Y_inact,PETSC_NULL,&scat_inact);
1228: /* Do a vec scatter to active and inactive set vectors */
1229: VecScatterBegin(scat_inact,F,F_inact,INSERT_VALUES,SCATTER_FORWARD);
1230: VecScatterEnd(scat_inact,F,F_inact,INSERT_VALUES,SCATTER_FORWARD);
1232: VecScatterBegin(scat_act,Y,Y_act,INSERT_VALUES,SCATTER_FORWARD);
1233: VecScatterEnd(scat_act,Y,Y_act,INSERT_VALUES,SCATTER_FORWARD);
1235: VecScatterBegin(scat_inact,Y,Y_inact,INSERT_VALUES,SCATTER_FORWARD);
1236: VecScatterEnd(scat_inact,Y,Y_inact,INSERT_VALUES,SCATTER_FORWARD);
1237:
1238: /* Active set direction = 0 */
1239: VecSet(Y_act,0);
1240: if (snes->jacobian != snes->jacobian_pre) {
1241: MatGetSubMatrix(snes->jacobian_pre,IS_inact,IS_inact,MAT_INITIAL_MATRIX,&prejac_inact_inact);
1242: } else prejac_inact_inact = jac_inact_inact;
1244: ISEqual(vi->IS_inact_prev,IS_inact,&isequal);
1245: if (!isequal) {
1246: SNESVIResetPCandKSP(snes,jac_inact_inact,prejac_inact_inact);
1247: flg = DIFFERENT_NONZERO_PATTERN;
1248: }
1249:
1250: /* ISView(IS_inact,0); */
1251: /* ISView(IS_act,0);*/
1252: /* MatView(snes->jacobian_pre,0); */
1254:
1255:
1256: KSPSetOperators(snes->ksp,jac_inact_inact,prejac_inact_inact,flg);
1257: KSPSetUp(snes->ksp);
1258: {
1259: PC pc;
1260: PetscBool flg;
1261: KSPGetPC(snes->ksp,&pc);
1262: PetscTypeCompare((PetscObject)pc,PCFIELDSPLIT,&flg);
1263: if (flg) {
1264: KSP *subksps;
1265: PCFieldSplitGetSubKSP(pc,PETSC_NULL,&subksps);
1266: KSPGetPC(subksps[0],&pc);
1267: PetscFree(subksps);
1268: PetscTypeCompare((PetscObject)pc,PCBJACOBI,&flg);
1269: if (flg) {
1270: PetscInt n,N = 101*101,j,cnts[3] = {0,0,0};
1271: const PetscInt *ii;
1273: ISGetSize(IS_inact,&n);
1274: ISGetIndices(IS_inact,&ii);
1275: for (j=0; j<n; j++) {
1276: if (ii[j] < N) cnts[0]++;
1277: else if (ii[j] < 2*N) cnts[1]++;
1278: else if (ii[j] < 3*N) cnts[2]++;
1279: }
1280: ISRestoreIndices(IS_inact,&ii);
1282: PCBJacobiSetTotalBlocks(pc,3,cnts);
1283: }
1284: }
1285: }
1287: SNES_KSPSolve(snes,snes->ksp,F_inact,Y_inact);
1288: KSPGetConvergedReason(snes->ksp,&kspreason);
1289: if (kspreason < 0) {
1290: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
1291: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
1292: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
1293: break;
1294: }
1295: }
1297: VecScatterBegin(scat_act,Y_act,Y,INSERT_VALUES,SCATTER_REVERSE);
1298: VecScatterEnd(scat_act,Y_act,Y,INSERT_VALUES,SCATTER_REVERSE);
1299: VecScatterBegin(scat_inact,Y_inact,Y,INSERT_VALUES,SCATTER_REVERSE);
1300: VecScatterEnd(scat_inact,Y_inact,Y,INSERT_VALUES,SCATTER_REVERSE);
1302: VecDestroy(&F_inact);
1303: VecDestroy(&Y_act);
1304: VecDestroy(&Y_inact);
1305: VecScatterDestroy(&scat_act);
1306: VecScatterDestroy(&scat_inact);
1307: ISDestroy(&IS_act);
1308: if (!isequal) {
1309: ISDestroy(&vi->IS_inact_prev);
1310: ISDuplicate(IS_inact,&vi->IS_inact_prev);
1311: }
1312: ISDestroy(&IS_inact);
1313: MatDestroy(&jac_inact_inact);
1314: if (snes->jacobian != snes->jacobian_pre) {
1315: MatDestroy(&prejac_inact_inact);
1316: }
1317: KSPGetIterationNumber(snes->ksp,&lits);
1318: snes->linear_its += lits;
1319: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
1320: /*
1321: if (vi->precheckstep) {
1322: PetscBool changed_y = PETSC_FALSE;
1323: (*vi->precheckstep)(snes,X,Y,vi->precheck,&changed_y);
1324: }
1326: if (PetscLogPrintInfo){
1327: SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
1328: }
1329: */
1330: /* Compute a (scaled) negative update in the line search routine:
1331: Y <- X - lambda*Y
1332: and evaluate G = function(Y) (depends on the line search).
1333: */
1334: VecCopy(Y,snes->vec_sol_update);
1335: ynorm = 1; gnorm = fnorm;
1336: (*vi->LineSearch)(snes,vi->lsP,X,F,G,Y,W,fnorm,xnorm,&ynorm,&gnorm,&lssucceed);
1337: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",fnorm,gnorm,ynorm,(int)lssucceed);
1338: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
1339: if (snes->domainerror) {
1340: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1341: DMDestroyVI(snes->dm);
1342: return(0);
1343: }
1344: if (!lssucceed) {
1345: if (++snes->numFailures >= snes->maxFailures) {
1346: PetscBool ismin;
1347: snes->reason = SNES_DIVERGED_LINE_SEARCH;
1348: SNESVICheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);
1349: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
1350: break;
1351: }
1352: }
1353: /* Update function and solution vectors */
1354: fnorm = gnorm;
1355: VecCopy(G,F);
1356: VecCopy(W,X);
1357: /* Monitor convergence */
1358: PetscObjectTakeAccess(snes);
1359: snes->iter = i+1;
1360: snes->norm = fnorm;
1361: PetscObjectGrantAccess(snes);
1362: SNESLogConvHistory(snes,snes->norm,lits);
1363: SNESMonitor(snes,snes->iter,snes->norm);
1364: /* Test for convergence, xnorm = || X || */
1365: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
1366: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
1367: if (snes->reason) break;
1368: }
1369: DMDestroyVI(snes->dm);
1370: if (i == maxits) {
1371: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
1372: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
1373: }
1374: return(0);
1375: }
1379: PetscErrorCode SNESVISetRedundancyCheck(SNES snes,PetscErrorCode (*func)(SNES,IS,IS*,void*),void *ctx)
1380: {
1381: SNES_VI *vi = (SNES_VI*)snes->data;
1385: vi->checkredundancy = func;
1386: vi->ctxP = ctx;
1387: return(0);
1388: }
1390: #if defined(PETSC_HAVE_MATLAB_ENGINE)
1391: #include <engine.h>
1392: #include <mex.h>
1393: typedef struct {char *funcname; mxArray *ctx;} SNESMatlabContext;
1397: PetscErrorCode SNESVIRedundancyCheck_Matlab(SNES snes,IS is_act,IS* is_redact,void* ctx)
1398: {
1399: PetscErrorCode ierr;
1400: SNESMatlabContext *sctx = (SNESMatlabContext*)ctx;
1401: int nlhs = 1, nrhs = 5;
1402: mxArray *plhs[1], *prhs[5];
1403: long long int l1 = 0, l2 = 0, ls = 0;
1404: PetscInt *indices=PETSC_NULL;
1412: /* Create IS for reduced active set of size 0, its size and indices will
1413: bet set by the Matlab function */
1414: ISCreateGeneral(((PetscObject)snes)->comm,0,indices,PETSC_OWN_POINTER,is_redact);
1415: /* call Matlab function in ctx */
1416: PetscMemcpy(&ls,&snes,sizeof(snes));
1417: PetscMemcpy(&l1,&is_act,sizeof(is_act));
1418: PetscMemcpy(&l2,is_redact,sizeof(is_act));
1419: prhs[0] = mxCreateDoubleScalar((double)ls);
1420: prhs[1] = mxCreateDoubleScalar((double)l1);
1421: prhs[2] = mxCreateDoubleScalar((double)l2);
1422: prhs[3] = mxCreateString(sctx->funcname);
1423: prhs[4] = sctx->ctx;
1424: mexCallMATLAB(nlhs,plhs,nrhs,prhs,"PetscSNESVIRedundancyCheckInternal");
1425: mxGetScalar(plhs[0]);
1426: mxDestroyArray(prhs[0]);
1427: mxDestroyArray(prhs[1]);
1428: mxDestroyArray(prhs[2]);
1429: mxDestroyArray(prhs[3]);
1430: mxDestroyArray(plhs[0]);
1431: return(0);
1432: }
1436: PetscErrorCode SNESVISetRedundancyCheckMatlab(SNES snes,const char* func,mxArray* ctx)
1437: {
1438: PetscErrorCode ierr;
1439: SNESMatlabContext *sctx;
1442: /* currently sctx is memory bleed */
1443: PetscMalloc(sizeof(SNESMatlabContext),&sctx);
1444: PetscStrallocpy(func,&sctx->funcname);
1445: sctx->ctx = mxDuplicateArray(ctx);
1446: SNESVISetRedundancyCheck(snes,SNESVIRedundancyCheck_Matlab,sctx);
1447: return(0);
1448: }
1449:
1450: #endif
1453: /* Variational Inequality solver using augmented space method. It does the opposite of the
1454: reduced space method i.e. it identifies the active set variables and instead of discarding
1455: them it augments the original system by introducing additional equality
1456: constraint equations for active set variables. The user can optionally provide an IS for
1457: a subset of 'redundant' active set variables which will treated as free variables.
1458: Specific implementation for Allen-Cahn problem
1459: */
1462: PetscErrorCode SNESSolveVI_RSAUG(SNES snes)
1463: {
1464: SNES_VI *vi = (SNES_VI*)snes->data;
1465: PetscErrorCode ierr;
1466: PetscInt maxits,i,lits;
1467: PetscBool lssucceed;
1468: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
1469: PetscReal fnorm,gnorm,xnorm=0,ynorm;
1470: Vec Y,X,F,G,W;
1471: KSPConvergedReason kspreason;
1474: snes->numFailures = 0;
1475: snes->numLinearSolveFailures = 0;
1476: snes->reason = SNES_CONVERGED_ITERATING;
1478: maxits = snes->max_its; /* maximum number of iterations */
1479: X = snes->vec_sol; /* solution vector */
1480: F = snes->vec_func; /* residual vector */
1481: Y = snes->work[0]; /* work vectors */
1482: G = snes->work[1];
1483: W = snes->work[2];
1485: PetscObjectTakeAccess(snes);
1486: snes->iter = 0;
1487: snes->norm = 0.0;
1488: PetscObjectGrantAccess(snes);
1490: SNESVIProjectOntoBounds(snes,X);
1491: SNESComputeFunction(snes,X,F);
1492: if (snes->domainerror) {
1493: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1494: return(0);
1495: }
1496: SNESVIComputeInactiveSetFnorm(snes,F,X,&fnorm);
1497: VecNormBegin(X,NORM_2,&xnorm); /* xnorm <- ||x|| */
1498: VecNormEnd(X,NORM_2,&xnorm);
1499: if (PetscIsInfOrNanReal(fnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
1501: PetscObjectTakeAccess(snes);
1502: snes->norm = fnorm;
1503: PetscObjectGrantAccess(snes);
1504: SNESLogConvHistory(snes,fnorm,0);
1505: SNESMonitor(snes,0,fnorm);
1507: /* set parameter for default relative tolerance convergence test */
1508: snes->ttol = fnorm*snes->rtol;
1509: /* test convergence */
1510: (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
1511: if (snes->reason) return(0);
1513: for (i=0; i<maxits; i++) {
1514: IS IS_act,IS_inact; /* _act -> active set _inact -> inactive set */
1515: IS IS_redact; /* redundant active set */
1516: Mat J_aug,Jpre_aug;
1517: Vec F_aug,Y_aug;
1518: PetscInt nis_redact,nis_act;
1519: const PetscInt *idx_redact,*idx_act;
1520: PetscInt k;
1521: PetscInt *idx_actkept=PETSC_NULL,nkept=0; /* list of kept active set */
1522: PetscScalar *f,*f2;
1523: PetscBool isequal;
1524: PetscInt i1=0,j1=0;
1526: /* Call general purpose update function */
1527: if (snes->ops->update) {
1528: (*snes->ops->update)(snes, snes->iter);
1529: }
1530: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
1532: /* Create active and inactive index sets */
1533: SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&IS_inact);
1535: /* Get local active set size */
1536: ISGetLocalSize(IS_act,&nis_act);
1537: if (nis_act) {
1538: ISGetIndices(IS_act,&idx_act);
1539: IS_redact = PETSC_NULL;
1540: if(vi->checkredundancy) {
1541: (*vi->checkredundancy)(snes,IS_act,&IS_redact,vi->ctxP);
1542: }
1544: if(!IS_redact) {
1545: /* User called checkredundancy function but didn't create IS_redact because
1546: there were no redundant active set variables */
1547: /* Copy over all active set indices to the list */
1548: PetscMalloc(nis_act*sizeof(PetscInt),&idx_actkept);
1549: for(k=0;k < nis_act;k++) idx_actkept[k] = idx_act[k];
1550: nkept = nis_act;
1551: } else {
1552: ISGetLocalSize(IS_redact,&nis_redact);
1553: PetscMalloc((nis_act-nis_redact)*sizeof(PetscInt),&idx_actkept);
1555: /* Create reduced active set list */
1556: ISGetIndices(IS_act,&idx_act);
1557: ISGetIndices(IS_redact,&idx_redact);
1558: j1 = 0;nkept = 0;
1559: for(k=0;k<nis_act;k++) {
1560: if(j1 < nis_redact && idx_act[k] == idx_redact[j1]) j1++;
1561: else idx_actkept[nkept++] = idx_act[k];
1562: }
1563: ISRestoreIndices(IS_act,&idx_act);
1564: ISRestoreIndices(IS_redact,&idx_redact);
1566: ISDestroy(&IS_redact);
1567: }
1569: /* Create augmented F and Y */
1570: VecCreate(((PetscObject)snes)->comm,&F_aug);
1571: VecSetSizes(F_aug,F->map->n+nkept,PETSC_DECIDE);
1572: VecSetFromOptions(F_aug);
1573: VecDuplicate(F_aug,&Y_aug);
1574:
1575: /* Copy over F to F_aug in the first n locations */
1576: VecGetArray(F,&f);
1577: VecGetArray(F_aug,&f2);
1578: PetscMemcpy(f2,f,F->map->n*sizeof(PetscScalar));
1579: VecRestoreArray(F,&f);
1580: VecRestoreArray(F_aug,&f2);
1581:
1582: /* Create the augmented jacobian matrix */
1583: MatCreate(((PetscObject)snes)->comm,&J_aug);
1584: MatSetSizes(J_aug,X->map->n+nkept,X->map->n+nkept,PETSC_DECIDE,PETSC_DECIDE);
1585: MatSetFromOptions(J_aug);
1586:
1588: { /* local vars */
1589: /* Preallocate augmented matrix and set values in it...Doing only sequential case first*/
1590: PetscInt ncols;
1591: const PetscInt *cols;
1592: const PetscScalar *vals;
1593: PetscScalar value[2];
1594: PetscInt row,col[2];
1595: PetscInt *d_nnz;
1596: value[0] = 1.0; value[1] = 0.0;
1597: PetscMalloc((X->map->n+nkept)*sizeof(PetscInt),&d_nnz);
1598: PetscMemzero(d_nnz,(X->map->n+nkept)*sizeof(PetscInt));
1599: for(row=0;row<snes->jacobian->rmap->n;row++) {
1600: MatGetRow(snes->jacobian,row,&ncols,PETSC_NULL,PETSC_NULL);
1601: d_nnz[row] += ncols;
1602: MatRestoreRow(snes->jacobian,row,&ncols,PETSC_NULL,PETSC_NULL);
1603: }
1605: for(k=0;k<nkept;k++) {
1606: d_nnz[idx_actkept[k]] += 1;
1607: d_nnz[snes->jacobian->rmap->n+k] += 2;
1608: }
1609: MatSeqAIJSetPreallocation(J_aug,PETSC_NULL,d_nnz);
1610:
1611: PetscFree(d_nnz);
1613: /* Set the original jacobian matrix in J_aug */
1614: for(row=0;row<snes->jacobian->rmap->n;row++) {
1615: MatGetRow(snes->jacobian,row,&ncols,&cols,&vals);
1616: MatSetValues(J_aug,1,&row,ncols,cols,vals,INSERT_VALUES);
1617: MatRestoreRow(snes->jacobian,row,&ncols,&cols,&vals);
1618: }
1619: /* Add the augmented part */
1620: for(k=0;k<nkept;k++) {
1621: row = snes->jacobian->rmap->n+k;
1622: col[0] = idx_actkept[k]; col[1] = snes->jacobian->rmap->n+k;
1623: MatSetValues(J_aug,1,&row,1,col,value,INSERT_VALUES);
1624: MatSetValues(J_aug,1,&col[0],1,&row,&value[0],INSERT_VALUES);
1625: }
1626: MatAssemblyBegin(J_aug,MAT_FINAL_ASSEMBLY);
1627: MatAssemblyEnd(J_aug,MAT_FINAL_ASSEMBLY);
1628: /* Only considering prejac = jac for now */
1629: Jpre_aug = J_aug;
1630: } /* local vars*/
1631: ISRestoreIndices(IS_act,&idx_act);
1632: ISDestroy(&IS_act);
1633: } else {
1634: F_aug = F; J_aug = snes->jacobian; Y_aug = Y; Jpre_aug = snes->jacobian_pre;
1635: }
1637: ISEqual(vi->IS_inact_prev,IS_inact,&isequal);
1638: if (!isequal) {
1639: SNESVIResetPCandKSP(snes,J_aug,Jpre_aug);
1640: }
1641: KSPSetOperators(snes->ksp,J_aug,Jpre_aug,flg);
1642: KSPSetUp(snes->ksp);
1643: /* {
1644: PC pc;
1645: PetscBool flg;
1646: KSPGetPC(snes->ksp,&pc);
1647: PetscTypeCompare((PetscObject)pc,PCFIELDSPLIT,&flg);
1648: if (flg) {
1649: KSP *subksps;
1650: PCFieldSplitGetSubKSP(pc,PETSC_NULL,&subksps);
1651: KSPGetPC(subksps[0],&pc);
1652: PetscFree(subksps);
1653: PetscTypeCompare((PetscObject)pc,PCBJACOBI,&flg);
1654: if (flg) {
1655: PetscInt n,N = 101*101,j,cnts[3] = {0,0,0};
1656: const PetscInt *ii;
1658: ISGetSize(IS_inact,&n);
1659: ISGetIndices(IS_inact,&ii);
1660: for (j=0; j<n; j++) {
1661: if (ii[j] < N) cnts[0]++;
1662: else if (ii[j] < 2*N) cnts[1]++;
1663: else if (ii[j] < 3*N) cnts[2]++;
1664: }
1665: ISRestoreIndices(IS_inact,&ii);
1667: PCBJacobiSetTotalBlocks(pc,3,cnts);
1668: }
1669: }
1670: }
1671: */
1672: SNES_KSPSolve(snes,snes->ksp,F_aug,Y_aug);
1673: KSPGetConvergedReason(snes->ksp,&kspreason);
1674: if (kspreason < 0) {
1675: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
1676: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
1677: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
1678: break;
1679: }
1680: }
1682: if(nis_act) {
1683: PetscScalar *y1,*y2;
1684: VecGetArray(Y,&y1);
1685: VecGetArray(Y_aug,&y2);
1686: /* Copy over inactive Y_aug to Y */
1687: j1 = 0;
1688: for(i1=Y->map->rstart;i1 < Y->map->rend;i1++) {
1689: if(j1 < nkept && idx_actkept[j1] == i1) j1++;
1690: else y1[i1-Y->map->rstart] = y2[i1-Y->map->rstart];
1691: }
1692: VecRestoreArray(Y,&y1);
1693: VecRestoreArray(Y_aug,&y2);
1695: VecDestroy(&F_aug);
1696: VecDestroy(&Y_aug);
1697: MatDestroy(&J_aug);
1698: PetscFree(idx_actkept);
1699: }
1700:
1701: if (!isequal) {
1702: ISDestroy(&vi->IS_inact_prev);
1703: ISDuplicate(IS_inact,&vi->IS_inact_prev);
1704: }
1705: ISDestroy(&IS_inact);
1707: KSPGetIterationNumber(snes->ksp,&lits);
1708: snes->linear_its += lits;
1709: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
1710: /*
1711: if (vi->precheckstep) {
1712: PetscBool changed_y = PETSC_FALSE;
1713: (*vi->precheckstep)(snes,X,Y,vi->precheck,&changed_y);
1714: }
1716: if (PetscLogPrintInfo){
1717: SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
1718: }
1719: */
1720: /* Compute a (scaled) negative update in the line search routine:
1721: Y <- X - lambda*Y
1722: and evaluate G = function(Y) (depends on the line search).
1723: */
1724: VecCopy(Y,snes->vec_sol_update);
1725: ynorm = 1; gnorm = fnorm;
1726: (*vi->LineSearch)(snes,vi->lsP,X,F,G,Y,W,fnorm,xnorm,&ynorm,&gnorm,&lssucceed);
1727: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",fnorm,gnorm,ynorm,(int)lssucceed);
1728: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
1729: if (snes->domainerror) {
1730: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1731: return(0);
1732: }
1733: if (!lssucceed) {
1734: if (++snes->numFailures >= snes->maxFailures) {
1735: PetscBool ismin;
1736: snes->reason = SNES_DIVERGED_LINE_SEARCH;
1737: SNESVICheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);
1738: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
1739: break;
1740: }
1741: }
1742: /* Update function and solution vectors */
1743: fnorm = gnorm;
1744: VecCopy(G,F);
1745: VecCopy(W,X);
1746: /* Monitor convergence */
1747: PetscObjectTakeAccess(snes);
1748: snes->iter = i+1;
1749: snes->norm = fnorm;
1750: PetscObjectGrantAccess(snes);
1751: SNESLogConvHistory(snes,snes->norm,lits);
1752: SNESMonitor(snes,snes->iter,snes->norm);
1753: /* Test for convergence, xnorm = || X || */
1754: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
1755: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
1756: if (snes->reason) break;
1757: }
1758: if (i == maxits) {
1759: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
1760: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
1761: }
1762: return(0);
1763: }
1765: /* -------------------------------------------------------------------------- */
1766: /*
1767: SNESSetUp_VI - Sets up the internal data structures for the later use
1768: of the SNESVI nonlinear solver.
1770: Input Parameter:
1771: . snes - the SNES context
1772: . x - the solution vector
1774: Application Interface Routine: SNESSetUp()
1776: Notes:
1777: For basic use of the SNES solvers, the user need not explicitly call
1778: SNESSetUp(), since these actions will automatically occur during
1779: the call to SNESSolve().
1780: */
1783: PetscErrorCode SNESSetUp_VI(SNES snes)
1784: {
1786: SNES_VI *vi = (SNES_VI*) snes->data;
1787: PetscInt i_start[3],i_end[3];
1791: SNESDefaultGetWork(snes,3);
1793: if (vi->computevariablebounds) {
1794: if (!vi->xl) {VecDuplicate(snes->vec_sol,&vi->xl);}
1795: if (!vi->xu) {VecDuplicate(snes->vec_sol,&vi->xu);}
1796: (*vi->computevariablebounds)(snes,vi->xl,vi->xu);
1797: } else if (!vi->xl && !vi->xu) {
1798: /* If the lower and upper bound on variables are not set, set it to -Inf and Inf */
1799: VecDuplicate(snes->vec_sol, &vi->xl);
1800: VecSet(vi->xl,SNES_VI_NINF);
1801: VecDuplicate(snes->vec_sol, &vi->xu);
1802: VecSet(vi->xu,SNES_VI_INF);
1803: } else {
1804: /* Check if lower bound, upper bound and solution vector distribution across the processors is identical */
1805: VecGetOwnershipRange(snes->vec_sol,i_start,i_end);
1806: VecGetOwnershipRange(vi->xl,i_start+1,i_end+1);
1807: VecGetOwnershipRange(vi->xu,i_start+2,i_end+2);
1808: 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]))
1809: 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.");
1810: }
1811: if (snes->ops->solve != SNESSolveVI_SS) {
1812: /* Set up previous active index set for the first snes solve
1813: vi->IS_inact_prev = 0,1,2,....N */
1814: PetscInt *indices;
1815: PetscInt i,n,rstart,rend;
1817: VecGetOwnershipRange(snes->vec_sol,&rstart,&rend);
1818: VecGetLocalSize(snes->vec_sol,&n);
1819: PetscMalloc(n*sizeof(PetscInt),&indices);
1820: for(i=0;i < n; i++) indices[i] = rstart + i;
1821: ISCreateGeneral(((PetscObject)snes)->comm,n,indices,PETSC_OWN_POINTER,&vi->IS_inact_prev);
1822: }
1824: if (snes->ops->solve == SNESSolveVI_SS) {
1825: VecDuplicate(snes->vec_sol, &vi->dpsi);
1826: VecDuplicate(snes->vec_sol, &vi->phi);
1827: VecDuplicate(snes->vec_sol, &vi->Da);
1828: VecDuplicate(snes->vec_sol, &vi->Db);
1829: VecDuplicate(snes->vec_sol, &vi->z);
1830: VecDuplicate(snes->vec_sol, &vi->t);
1831: }
1832: return(0);
1833: }
1834: /* -------------------------------------------------------------------------- */
1837: PetscErrorCode SNESReset_VI(SNES snes)
1838: {
1839: SNES_VI *vi = (SNES_VI*) snes->data;
1843: VecDestroy(&vi->xl);
1844: VecDestroy(&vi->xu);
1845: if (snes->ops->solve != SNESSolveVI_SS) {
1846: ISDestroy(&vi->IS_inact_prev);
1847: }
1848: return(0);
1849: }
1851: /*
1852: SNESDestroy_VI - Destroys the private SNES_VI context that was created
1853: with SNESCreate_VI().
1855: Input Parameter:
1856: . snes - the SNES context
1858: Application Interface Routine: SNESDestroy()
1859: */
1862: PetscErrorCode SNESDestroy_VI(SNES snes)
1863: {
1864: SNES_VI *vi = (SNES_VI*) snes->data;
1869: if (snes->ops->solve == SNESSolveVI_SS) {
1870: /* clear vectors */
1871: VecDestroy(&vi->dpsi);
1872: VecDestroy(&vi->phi);
1873: VecDestroy(&vi->Da);
1874: VecDestroy(&vi->Db);
1875: VecDestroy(&vi->z);
1876: VecDestroy(&vi->t);
1877: }
1878:
1879: PetscViewerDestroy(&vi->lsmonitor);
1880: PetscFree(snes->data);
1882: /* clear composed functions */
1883: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","",PETSC_NULL);
1884: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetMonitor_C","",PETSC_NULL);
1885: return(0);
1886: }
1888: /* -------------------------------------------------------------------------- */
1892: /*
1893: This routine does not actually do a line search but it takes a full newton
1894: step while ensuring that the new iterates remain within the constraints.
1895:
1896: */
1897: PetscErrorCode SNESLineSearchNo_VI(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal xnorm,PetscReal *ynorm,PetscReal *gnorm,PetscBool *flag)
1898: {
1900: SNES_VI *vi = (SNES_VI*)snes->data;
1901: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
1904: *flag = PETSC_TRUE;
1905: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
1906: VecNorm(y,NORM_2,ynorm); /* ynorm = || y || */
1907: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1908: SNESVIProjectOntoBounds(snes,w);
1909: if (vi->postcheckstep) {
1910: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
1911: }
1912: if (changed_y) {
1913: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1914: SNESVIProjectOntoBounds(snes,w);
1915: }
1916: SNESVIProjectOntoBounds(snes,w);
1917: SNESComputeFunction(snes,w,g);
1918: if (!snes->domainerror) {
1919: if (snes->ops->solve != SNESSolveVI_SS) {
1920: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
1921: } else {
1922: VecNorm(g,NORM_2,gnorm); /* gnorm = || g || */
1923: }
1924: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
1925: }
1926: if (vi->lsmonitor) {
1927: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1928: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Using full step: fnorm %g gnorm %g\n",(double)fnorm,(double)*gnorm);
1929: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1930: }
1931: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
1932: return(0);
1933: }
1935: /* -------------------------------------------------------------------------- */
1939: /*
1940: This routine is a copy of SNESLineSearchNoNorms in snes/impls/ls/ls.c
1941: */
1942: PetscErrorCode SNESLineSearchNoNorms_VI(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal xnorm,PetscReal *ynorm,PetscReal *gnorm,PetscBool *flag)
1943: {
1945: SNES_VI *vi = (SNES_VI*)snes->data;
1946: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
1949: *flag = PETSC_TRUE;
1950: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
1951: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1952: SNESVIProjectOntoBounds(snes,w);
1953: if (vi->postcheckstep) {
1954: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
1955: }
1956: if (changed_y) {
1957: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1958: SNESVIProjectOntoBounds(snes,w);
1959: }
1960:
1961: /* don't evaluate function the last time through */
1962: if (snes->iter < snes->max_its-1) {
1963: SNESComputeFunction(snes,w,g);
1964: }
1965: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
1966: return(0);
1967: }
1969: /* -------------------------------------------------------------------------- */
1972: /*
1973: This routine implements a cubic line search while doing a projection on the variable bounds
1974: */
1975: PetscErrorCode SNESLineSearchCubic_VI(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal xnorm,PetscReal *ynorm,PetscReal *gnorm,PetscBool *flag)
1976: {
1977: PetscReal initslope,lambdaprev,gnormprev,a,b,d,t1,t2,rellength;
1978: PetscReal minlambda,lambda,lambdatemp;
1979: #if defined(PETSC_USE_COMPLEX)
1980: PetscScalar cinitslope;
1981: #endif
1983: PetscInt count;
1984: SNES_VI *vi = (SNES_VI*)snes->data;
1985: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
1986: MPI_Comm comm;
1989: PetscObjectGetComm((PetscObject)snes,&comm);
1990: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
1991: *flag = PETSC_TRUE;
1993: VecNorm(y,NORM_2,ynorm);
1994: if (*ynorm == 0.0) {
1995: if (vi->lsmonitor) {
1996: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1997: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Initial direction and size is 0\n");
1998: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1999: }
2000: *gnorm = fnorm;
2001: VecCopy(x,w);
2002: VecCopy(f,g);
2003: *flag = PETSC_FALSE;
2004: goto theend1;
2005: }
2006: if (*ynorm > vi->maxstep) { /* Step too big, so scale back */
2007: if (vi->lsmonitor) {
2008: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2009: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Scaling step by %g old ynorm %g\n",(double)vi->maxstep/(*ynorm),(double)*ynorm);
2010: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2011: }
2012: VecScale(y,vi->maxstep/(*ynorm));
2013: *ynorm = vi->maxstep;
2014: }
2015: VecMaxPointwiseDivide(y,x,&rellength);
2016: minlambda = vi->minlambda/rellength;
2017: MatMult(snes->jacobian,y,w);
2018: #if defined(PETSC_USE_COMPLEX)
2019: VecDot(f,w,&cinitslope);
2020: initslope = PetscRealPart(cinitslope);
2021: #else
2022: VecDot(f,w,&initslope);
2023: #endif
2024: if (initslope > 0.0) initslope = -initslope;
2025: if (initslope == 0.0) initslope = -1.0;
2027: VecWAXPY(w,-1.0,y,x);
2028: SNESVIProjectOntoBounds(snes,w);
2029: if (snes->nfuncs >= snes->max_funcs) {
2030: PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");
2031: *flag = PETSC_FALSE;
2032: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2033: goto theend1;
2034: }
2035: SNESComputeFunction(snes,w,g);
2036: if (snes->ops->solve != SNESSolveVI_SS) {
2037: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2038: } else {
2039: VecNorm(g,NORM_2,gnorm);
2040: }
2041: if (snes->domainerror) {
2042: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2043: return(0);
2044: }
2045: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2046: PetscInfo4(snes,"Initial fnorm %g gnorm %g alpha %g initslope %g\n",(double)fnorm,(double)*gnorm,(double)vi->alpha,(double)initslope);
2047: if ((*gnorm)*(*gnorm) <= (1.0 - vi->alpha)*fnorm*fnorm ) { /* Sufficient reduction */
2048: if (vi->lsmonitor) {
2049: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2050: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Using full step: fnorm %g gnorm %g\n",(double)fnorm,(double)*gnorm);
2051: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2052: }
2053: goto theend1;
2054: }
2056: /* Fit points with quadratic */
2057: lambda = 1.0;
2058: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
2059: lambdaprev = lambda;
2060: gnormprev = *gnorm;
2061: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
2062: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
2063: else lambda = lambdatemp;
2065: VecWAXPY(w,-lambda,y,x);
2066: SNESVIProjectOntoBounds(snes,w);
2067: if (snes->nfuncs >= snes->max_funcs) {
2068: PetscInfo1(snes,"Exceeded maximum function evaluations, while attempting quadratic backtracking! %D \n",snes->nfuncs);
2069: *flag = PETSC_FALSE;
2070: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2071: goto theend1;
2072: }
2073: SNESComputeFunction(snes,w,g);
2074: if (snes->ops->solve != SNESSolveVI_SS) {
2075: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2076: } else {
2077: VecNorm(g,NORM_2,gnorm);
2078: }
2079: if (snes->domainerror) {
2080: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2081: return(0);
2082: }
2083: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2084: if (vi->lsmonitor) {
2085: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2086: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: gnorm after quadratic fit %g\n",(double)*gnorm);
2087: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2088: }
2089: if ((*gnorm)*(*gnorm) < (1.0 - vi->alpha)*fnorm*fnorm ) { /* sufficient reduction */
2090: if (vi->lsmonitor) {
2091: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2092: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Quadratically determined step, lambda=%18.16e\n",lambda);
2093: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2094: }
2095: goto theend1;
2096: }
2098: /* Fit points with cubic */
2099: count = 1;
2100: while (PETSC_TRUE) {
2101: if (lambda <= minlambda) {
2102: if (vi->lsmonitor) {
2103: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2104: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: unable to find good step length! After %D tries \n",count);
2105: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, minlambda=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,minlambda,lambda,initslope);
2106: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2107: }
2108: *flag = PETSC_FALSE;
2109: break;
2110: }
2111: t1 = .5*((*gnorm)*(*gnorm) - fnorm*fnorm) - lambda*initslope;
2112: t2 = .5*(gnormprev*gnormprev - fnorm*fnorm) - lambdaprev*initslope;
2113: a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2114: b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2115: d = b*b - 3*a*initslope;
2116: if (d < 0.0) d = 0.0;
2117: if (a == 0.0) {
2118: lambdatemp = -initslope/(2.0*b);
2119: } else {
2120: lambdatemp = (-b + sqrt(d))/(3.0*a);
2121: }
2122: lambdaprev = lambda;
2123: gnormprev = *gnorm;
2124: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
2125: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
2126: else lambda = lambdatemp;
2127: VecWAXPY(w,-lambda,y,x);
2128: SNESVIProjectOntoBounds(snes,w);
2129: if (snes->nfuncs >= snes->max_funcs) {
2130: PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);
2131: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
2132: *flag = PETSC_FALSE;
2133: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2134: break;
2135: }
2136: SNESComputeFunction(snes,w,g);
2137: if (snes->ops->solve != SNESSolveVI_SS) {
2138: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2139: } else {
2140: VecNorm(g,NORM_2,gnorm);
2141: }
2142: if (snes->domainerror) {
2143: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2144: return(0);
2145: }
2146: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2147: if ((*gnorm)*(*gnorm) < (1.0 - vi->alpha)*fnorm*fnorm) { /* is reduction enough? */
2148: if (vi->lsmonitor) {
2149: PetscPrintf(comm," Line search: Cubically determined step, current gnorm %g lambda=%18.16e\n",(double)*gnorm,(double)lambda);
2150: }
2151: break;
2152: } else {
2153: if (vi->lsmonitor) {
2154: PetscPrintf(comm," Line search: Cubic step no good, shrinking lambda, current gnorm %g lambda=%18.16e\n",(double)*gnorm,(double)lambda);
2155: }
2156: }
2157: count++;
2158: }
2159: theend1:
2160: /* Optional user-defined check for line search step validity */
2161: if (vi->postcheckstep && *flag) {
2162: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
2163: if (changed_y) {
2164: VecWAXPY(w,-1.0,y,x);
2165: SNESVIProjectOntoBounds(snes,w);
2166: }
2167: if (changed_y || changed_w) { /* recompute the function if the step has changed */
2168: SNESComputeFunction(snes,w,g);
2169: if (snes->ops->solve != SNESSolveVI_SS) {
2170: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2171: } else {
2172: VecNorm(g,NORM_2,gnorm);
2173: }
2174: if (snes->domainerror) {
2175: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2176: return(0);
2177: }
2178: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2179: VecNormBegin(y,NORM_2,ynorm);
2180: VecNormEnd(y,NORM_2,ynorm);
2181: }
2182: }
2183: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2184: return(0);
2185: }
2189: /*
2190: This routine does a quadratic line search while keeping the iterates within the variable bounds
2191: */
2192: PetscErrorCode SNESLineSearchQuadratic_VI(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal xnorm,PetscReal *ynorm,PetscReal *gnorm,PetscBool *flag)
2193: {
2194: /*
2195: Note that for line search purposes we work with with the related
2196: minimization problem:
2197: min z(x): R^n -> R,
2198: where z(x) = .5 * fnorm*fnorm,and fnorm = || f ||_2.
2199: */
2200: PetscReal initslope,minlambda,lambda,lambdatemp,rellength;
2201: #if defined(PETSC_USE_COMPLEX)
2202: PetscScalar cinitslope;
2203: #endif
2205: PetscInt count;
2206: SNES_VI *vi = (SNES_VI*)snes->data;
2207: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
2210: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
2211: *flag = PETSC_TRUE;
2213: VecNorm(y,NORM_2,ynorm);
2214: if (*ynorm == 0.0) {
2215: if (vi->lsmonitor) {
2216: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2217: PetscViewerASCIIPrintf(vi->lsmonitor,"Line search: Direction and size is 0\n");
2218: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2219: }
2220: *gnorm = fnorm;
2221: VecCopy(x,w);
2222: VecCopy(f,g);
2223: *flag = PETSC_FALSE;
2224: goto theend2;
2225: }
2226: if (*ynorm > vi->maxstep) { /* Step too big, so scale back */
2227: VecScale(y,vi->maxstep/(*ynorm));
2228: *ynorm = vi->maxstep;
2229: }
2230: VecMaxPointwiseDivide(y,x,&rellength);
2231: minlambda = vi->minlambda/rellength;
2232: MatMult(snes->jacobian,y,w);
2233: #if defined(PETSC_USE_COMPLEX)
2234: VecDot(f,w,&cinitslope);
2235: initslope = PetscRealPart(cinitslope);
2236: #else
2237: VecDot(f,w,&initslope);
2238: #endif
2239: if (initslope > 0.0) initslope = -initslope;
2240: if (initslope == 0.0) initslope = -1.0;
2241: PetscInfo1(snes,"Initslope %G \n",initslope);
2243: VecWAXPY(w,-1.0,y,x);
2244: SNESVIProjectOntoBounds(snes,w);
2245: if (snes->nfuncs >= snes->max_funcs) {
2246: PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");
2247: *flag = PETSC_FALSE;
2248: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2249: goto theend2;
2250: }
2251: SNESComputeFunction(snes,w,g);
2252: if (snes->ops->solve != SNESSolveVI_SS) {
2253: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2254: } else {
2255: VecNorm(g,NORM_2,gnorm);
2256: }
2257: if (snes->domainerror) {
2258: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2259: return(0);
2260: }
2261: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2262: if ((*gnorm)*(*gnorm) <= (1.0 - vi->alpha)*fnorm*fnorm) { /* Sufficient reduction */
2263: if (vi->lsmonitor) {
2264: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2265: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Using full step: fnorm %G gnorm %G\n",fnorm,*gnorm);
2266: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2267: }
2268: goto theend2;
2269: }
2271: /* Fit points with quadratic */
2272: lambda = 1.0;
2273: count = 1;
2274: while (PETSC_TRUE) {
2275: if (lambda <= minlambda) { /* bad luck; use full step */
2276: if (vi->lsmonitor) {
2277: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2278: PetscViewerASCIIPrintf(vi->lsmonitor,"Line search: Unable to find good step length! %D \n",count);
2279: PetscViewerASCIIPrintf(vi->lsmonitor,"Line search: fnorm=%G, gnorm=%G, ynorm=%G, lambda=%G, initial slope=%G\n",fnorm,*gnorm,*ynorm,lambda,initslope);
2280: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2281: }
2282: VecCopy(x,w);
2283: *flag = PETSC_FALSE;
2284: break;
2285: }
2286: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
2287: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
2288: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
2289: else lambda = lambdatemp;
2290:
2291: VecWAXPY(w,-lambda,y,x);
2292: SNESVIProjectOntoBounds(snes,w);
2293: if (snes->nfuncs >= snes->max_funcs) {
2294: PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);
2295: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
2296: *flag = PETSC_FALSE;
2297: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2298: break;
2299: }
2300: SNESComputeFunction(snes,w,g);
2301: if (snes->domainerror) {
2302: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2303: return(0);
2304: }
2305: if (snes->ops->solve != SNESSolveVI_SS) {
2306: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2307: } else {
2308: VecNorm(g,NORM_2,gnorm);
2309: }
2310: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2311: if ((*gnorm)*(*gnorm) < (1.0 - vi->alpha)*fnorm*fnorm) { /* sufficient reduction */
2312: if (vi->lsmonitor) {
2313: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2314: PetscViewerASCIIPrintf(vi->lsmonitor," Line Search: Quadratically determined step, lambda=%G\n",lambda);
2315: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2316: }
2317: break;
2318: }
2319: count++;
2320: }
2321: theend2:
2322: /* Optional user-defined check for line search step validity */
2323: if (vi->postcheckstep) {
2324: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
2325: if (changed_y) {
2326: VecWAXPY(w,-1.0,y,x);
2327: SNESVIProjectOntoBounds(snes,w);
2328: }
2329: if (changed_y || changed_w) { /* recompute the function if the step has changed */
2330: SNESComputeFunction(snes,w,g);
2331: if (snes->domainerror) {
2332: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2333: return(0);
2334: }
2335: if (snes->ops->solve != SNESSolveVI_SS) {
2336: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2337: } else {
2338: VecNorm(g,NORM_2,gnorm);
2339: }
2341: VecNormBegin(y,NORM_2,ynorm);
2342: VecNormEnd(y,NORM_2,ynorm);
2343: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2344: }
2345: }
2346: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2347: return(0);
2348: }
2351: /* -------------------------------------------------------------------------- */
2355: PetscErrorCode SNESLineSearchSet_VI(SNES snes,FCN2 func,void *lsctx)
2356: {
2358: ((SNES_VI *)(snes->data))->LineSearch = func;
2359: ((SNES_VI *)(snes->data))->lsP = lsctx;
2360: return(0);
2361: }
2364: /* -------------------------------------------------------------------------- */
2368: PetscErrorCode SNESLineSearchSetMonitor_VI(SNES snes,PetscBool flg)
2369: {
2370: SNES_VI *vi = (SNES_VI*)snes->data;
2374: if (flg && !vi->lsmonitor) {
2375: PetscViewerASCIIOpen(((PetscObject)snes)->comm,"stdout",&vi->lsmonitor);
2376: } else if (!flg && vi->lsmonitor) {
2377: PetscViewerDestroy(&vi->lsmonitor);
2378: }
2379: return(0);
2380: }
2383: /*
2384: SNESView_VI - Prints info from the SNESVI data structure.
2386: Input Parameters:
2387: . SNES - the SNES context
2388: . viewer - visualization context
2390: Application Interface Routine: SNESView()
2391: */
2394: static PetscErrorCode SNESView_VI(SNES snes,PetscViewer viewer)
2395: {
2396: SNES_VI *vi = (SNES_VI *)snes->data;
2397: const char *cstr,*tstr;
2399: PetscBool iascii;
2402: PetscTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
2403: if (iascii) {
2404: if (vi->LineSearch == SNESLineSearchNo_VI) cstr = "SNESLineSearchNo";
2405: else if (vi->LineSearch == SNESLineSearchQuadratic_VI) cstr = "SNESLineSearchQuadratic";
2406: else if (vi->LineSearch == SNESLineSearchCubic_VI) cstr = "SNESLineSearchCubic";
2407: else cstr = "unknown";
2408: if (snes->ops->solve == SNESSolveVI_SS) tstr = "Semismooth";
2409: else if (snes->ops->solve == SNESSolveVI_RS) tstr = "Reduced Space";
2410: else if (snes->ops->solve == SNESSolveVI_RSAUG) tstr = "Reduced space with augmented variables";
2411: else tstr = "unknown";
2412: PetscViewerASCIIPrintf(viewer," VI algorithm: %s\n",tstr);
2413: PetscViewerASCIIPrintf(viewer," line search variant: %s\n",cstr);
2414: PetscViewerASCIIPrintf(viewer," alpha=%G, maxstep=%G, minlambda=%G\n",vi->alpha,vi->maxstep,vi->minlambda);
2415: }
2416: return(0);
2417: }
2421: /*@
2422: SNESVISetVariableBounds - Sets the lower and upper bounds for the solution vector. xl <= x <= xu.
2424: Input Parameters:
2425: . snes - the SNES context.
2426: . xl - lower bound.
2427: . xu - upper bound.
2429: Notes:
2430: If this routine is not called then the lower and upper bounds are set to
2431: SNES_VI_INF and SNES_VI_NINF respectively during SNESSetUp().
2433: Level: advanced
2435: @*/
2436: PetscErrorCode SNESVISetVariableBounds(SNES snes, Vec xl, Vec xu)
2437: {
2438: SNES_VI *vi;
2439: PetscErrorCode ierr;
2440: const PetscScalar *xxl,*xxu;
2441: PetscInt i,n, cnt = 0;
2447: if (!snes->vec_func) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Must call SNESSetFunction() first");
2448: 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);
2449: 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);
2450: SNESSetType(snes,SNESVI);
2451: vi = (SNES_VI*)snes->data;
2452: PetscObjectReference((PetscObject)xl);
2453: PetscObjectReference((PetscObject)xu);
2454: VecDestroy(&vi->xl);
2455: VecDestroy(&vi->xu);
2456: vi->xl = xl;
2457: vi->xu = xu;
2458: VecGetLocalSize(xl,&n);
2459: VecGetArrayRead(xl,&xxl);
2460: VecGetArrayRead(xu,&xxu);
2461: for (i=0; i<n; i++) {
2462: cnt += ((xxl[i] != SNES_VI_NINF) || (xxu[i] != SNES_VI_INF));
2463: }
2464: MPI_Allreduce(&cnt,&vi->ntruebounds,1,MPIU_INT,MPI_SUM,((PetscObject)snes)->comm);
2465: VecRestoreArrayRead(xl,&xxl);
2466: VecRestoreArrayRead(xu,&xxu);
2467: return(0);
2468: }
2470: /* -------------------------------------------------------------------------- */
2471: /*
2472: SNESSetFromOptions_VI - Sets various parameters for the SNESVI method.
2474: Input Parameter:
2475: . snes - the SNES context
2477: Application Interface Routine: SNESSetFromOptions()
2478: */
2481: static PetscErrorCode SNESSetFromOptions_VI(SNES snes)
2482: {
2483: SNES_VI *vi = (SNES_VI *)snes->data;
2484: const char *lses[] = {"basic","basicnonorms","quadratic","cubic"};
2485: const char *vies[] = {"ss","rs","rsaug"};
2487: PetscInt indx;
2488: PetscBool flg,set,flg2;
2491: PetscOptionsHead("SNES semismooth method options");
2492: PetscOptionsBool("-snes_vi_monitor","Monitor all non-active variables","None",PETSC_FALSE,&flg,0);
2493: if (flg) {
2494: SNESMonitorSet(snes,SNESMonitorVI,0,0);
2495: }
2496: PetscOptionsReal("-snes_ls_alpha","Function norm must decrease by","None",vi->alpha,&vi->alpha,0);
2497: PetscOptionsReal("-snes_ls_maxstep","Step must be less than","None",vi->maxstep,&vi->maxstep,0);
2498: PetscOptionsReal("-snes_ls_minlambda","Minimum lambda allowed","None",vi->minlambda,&vi->minlambda,0);
2499: PetscOptionsReal("-snes_vi_const_tol","constraint tolerance","None",vi->const_tol,&vi->const_tol,0);
2500: PetscOptionsBool("-snes_ls_monitor","Print progress of line searches","SNESLineSearchSetMonitor",vi->lsmonitor ? PETSC_TRUE : PETSC_FALSE,&flg,&set);
2501: if (set) {SNESLineSearchSetMonitor(snes,flg);}
2502: PetscOptionsEList("-snes_vi_type","Semismooth algorithm used","",vies,3,"rs",&indx,&flg2);
2503: if (flg2) {
2504: switch (indx) {
2505: case 0:
2506: snes->ops->solve = SNESSolveVI_SS;
2507: break;
2508: case 1:
2509: snes->ops->solve = SNESSolveVI_RS;
2510: break;
2511: case 2:
2512: snes->ops->solve = SNESSolveVI_RSAUG;
2513: }
2514: }
2515: PetscOptionsEList("-snes_ls","Line search used","SNESLineSearchSet",lses,4,"cubic",&indx,&flg);
2516: if (flg) {
2517: switch (indx) {
2518: case 0:
2519: SNESLineSearchSet(snes,SNESLineSearchNo_VI,PETSC_NULL);
2520: break;
2521: case 1:
2522: SNESLineSearchSet(snes,SNESLineSearchNoNorms_VI,PETSC_NULL);
2523: break;
2524: case 2:
2525: SNESLineSearchSet(snes,SNESLineSearchQuadratic_VI,PETSC_NULL);
2526: break;
2527: case 3:
2528: SNESLineSearchSet(snes,SNESLineSearchCubic_VI,PETSC_NULL);
2529: break;
2530: }
2531: }
2532: PetscOptionsBool("-snes_vi_ignore_function_sign","Ignore the sign of function for active constraints","None",vi->ignorefunctionsign,&vi->ignorefunctionsign,PETSC_NULL);
2533: PetscOptionsTail();
2534: return(0);
2535: }
2536: /* -------------------------------------------------------------------------- */
2537: /*MC
2538: SNESVI - Various solvers for variational inequalities based on Newton's method
2540: Options Database:
2541: + -snes_vi_type <ss,rs,rsaug> a semi-smooth solver, a reduced space active set method and a reduced space active set method that does not eliminate the active constraints from the Jacobian instead augments the Jacobian with
2542: additional variables that enforce the constraints
2543: - -snes_vi_monitor - prints the number of active constraints at each iteration.
2546: Level: beginner
2548: .seealso: SNESCreate(), SNES, SNESSetType(), SNESTR, SNESLineSearchSet(),
2549: SNESLineSearchSetPostCheck(), SNESLineSearchNo(), SNESLineSearchCubic(), SNESLineSearchQuadratic(),
2550: SNESLineSearchSet(), SNESLineSearchNoNorms(), SNESLineSearchSetPreCheck(), SNESLineSearchSetParams(), SNESLineSearchGetParams()
2552: M*/
2556: PetscErrorCode SNESCreate_VI(SNES snes)
2557: {
2559: SNES_VI *vi;
2562: snes->ops->reset = SNESReset_VI;
2563: snes->ops->setup = SNESSetUp_VI;
2564: snes->ops->solve = SNESSolveVI_RS;
2565: snes->ops->destroy = SNESDestroy_VI;
2566: snes->ops->setfromoptions = SNESSetFromOptions_VI;
2567: snes->ops->view = SNESView_VI;
2568: snes->ops->converged = SNESDefaultConverged_VI;
2570: PetscNewLog(snes,SNES_VI,&vi);
2571: snes->data = (void*)vi;
2572: vi->alpha = 1.e-4;
2573: vi->maxstep = 1.e8;
2574: vi->minlambda = 1.e-12;
2575: vi->LineSearch = SNESLineSearchCubic_VI;
2576: vi->lsP = PETSC_NULL;
2577: vi->postcheckstep = PETSC_NULL;
2578: vi->postcheck = PETSC_NULL;
2579: vi->precheckstep = PETSC_NULL;
2580: vi->precheck = PETSC_NULL;
2581: vi->const_tol = 2.2204460492503131e-16;
2582: vi->checkredundancy = PETSC_NULL;
2584: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetMonitor_C","SNESLineSearchSetMonitor_VI",SNESLineSearchSetMonitor_VI);
2585: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","SNESLineSearchSet_VI",SNESLineSearchSet_VI);
2587: return(0);
2588: }
2593: /*
2594: SNESVIGetInactiveSet - Gets the global indices for the inactive set variables (these correspond to the degrees of freedom the linear
2595: system is solved on)
2597: Input parameter
2598: . snes - the SNES context
2600: Output parameter
2601: . ISact - active set index set
2603: */
2604: PetscErrorCode SNESVIGetInactiveSet(SNES snes,IS* inact)
2605: {
2606: SNES_VI *vi = (SNES_VI*)snes->data;
2608: *inact = vi->IS_inact_prev;
2609: return(0);
2610: }