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: // if (keptrows) {
1182: PetscInt cnt,*nrows,k;
1183: const PetscInt *krows,*inact;
1184: PetscInt rstart=jac_inact_inact->rmap->rstart;
1186: MatDestroy(&jac_inact_inact);
1187: ISDestroy(&IS_act);
1189: ISGetLocalSize(keptrows,&cnt);
1190: ISGetIndices(keptrows,&krows);
1191: ISGetIndices(IS_inact,&inact);
1192: PetscMalloc(cnt*sizeof(PetscInt),&nrows);
1193: for (k=0; k<cnt; k++) {
1194: nrows[k] = inact[krows[k]-rstart];
1195: }
1196: ISRestoreIndices(keptrows,&krows);
1197: ISRestoreIndices(IS_inact,&inact);
1198: ISDestroy(&keptrows);
1199: ISDestroy(&IS_inact);
1200:
1201: ISCreateGeneral(PETSC_COMM_WORLD,cnt,nrows,PETSC_OWN_POINTER,&IS_inact);
1202: ISComplement(IS_inact,F->map->rstart,F->map->rend,&IS_act);
1203: MatGetSubMatrix(snes->jacobian,IS_inact,IS_inact,MAT_INITIAL_MATRIX,&jac_inact_inact);
1204: }
1205: DMSetVI(snes->dm,IS_inact);
1206: /* remove later */
1208: /*
1209: VecView(vi->xu,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1210: VecView(vi->xl,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1211: VecView(X,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1212: VecView(F,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1213: ISView(IS_inact,PETSC_VIEWER_BINARY_(PETSC_COMM_WORLD));
1214: */
1216: /* Get sizes of active and inactive sets */
1217: ISGetLocalSize(IS_act,&nis_act);
1218: ISGetLocalSize(IS_inact,&nis_inact);
1220: /* Create active and inactive set vectors */
1221: SNESVICreateSubVectors(snes,nis_inact,&F_inact);
1222: SNESVICreateSubVectors(snes,nis_act,&Y_act);
1223: SNESVICreateSubVectors(snes,nis_inact,&Y_inact);
1225: /* Create scatter contexts */
1226: VecScatterCreate(Y,IS_act,Y_act,PETSC_NULL,&scat_act);
1227: VecScatterCreate(Y,IS_inact,Y_inact,PETSC_NULL,&scat_inact);
1229: /* Do a vec scatter to active and inactive set vectors */
1230: VecScatterBegin(scat_inact,F,F_inact,INSERT_VALUES,SCATTER_FORWARD);
1231: VecScatterEnd(scat_inact,F,F_inact,INSERT_VALUES,SCATTER_FORWARD);
1233: VecScatterBegin(scat_act,Y,Y_act,INSERT_VALUES,SCATTER_FORWARD);
1234: VecScatterEnd(scat_act,Y,Y_act,INSERT_VALUES,SCATTER_FORWARD);
1236: VecScatterBegin(scat_inact,Y,Y_inact,INSERT_VALUES,SCATTER_FORWARD);
1237: VecScatterEnd(scat_inact,Y,Y_inact,INSERT_VALUES,SCATTER_FORWARD);
1238:
1239: /* Active set direction = 0 */
1240: VecSet(Y_act,0);
1241: if (snes->jacobian != snes->jacobian_pre) {
1242: MatGetSubMatrix(snes->jacobian_pre,IS_inact,IS_inact,MAT_INITIAL_MATRIX,&prejac_inact_inact);
1243: } else prejac_inact_inact = jac_inact_inact;
1245: ISEqual(vi->IS_inact_prev,IS_inact,&isequal);
1246: if (!isequal) {
1247: SNESVIResetPCandKSP(snes,jac_inact_inact,prejac_inact_inact);
1248: flg = DIFFERENT_NONZERO_PATTERN;
1249: }
1250:
1251: /* ISView(IS_inact,0); */
1252: /* ISView(IS_act,0);*/
1253: /* MatView(snes->jacobian_pre,0); */
1255:
1256:
1257: KSPSetOperators(snes->ksp,jac_inact_inact,prejac_inact_inact,flg);
1258: KSPSetUp(snes->ksp);
1259: {
1260: PC pc;
1261: PetscBool flg;
1262: KSPGetPC(snes->ksp,&pc);
1263: PetscTypeCompare((PetscObject)pc,PCFIELDSPLIT,&flg);
1264: if (flg) {
1265: KSP *subksps;
1266: PCFieldSplitGetSubKSP(pc,PETSC_NULL,&subksps);
1267: KSPGetPC(subksps[0],&pc);
1268: PetscFree(subksps);
1269: PetscTypeCompare((PetscObject)pc,PCBJACOBI,&flg);
1270: if (flg) {
1271: PetscInt n,N = 101*101,j,cnts[3] = {0,0,0};
1272: const PetscInt *ii;
1274: ISGetSize(IS_inact,&n);
1275: ISGetIndices(IS_inact,&ii);
1276: for (j=0; j<n; j++) {
1277: if (ii[j] < N) cnts[0]++;
1278: else if (ii[j] < 2*N) cnts[1]++;
1279: else if (ii[j] < 3*N) cnts[2]++;
1280: }
1281: ISRestoreIndices(IS_inact,&ii);
1283: PCBJacobiSetTotalBlocks(pc,3,cnts);
1284: }
1285: }
1286: }
1288: SNES_KSPSolve(snes,snes->ksp,F_inact,Y_inact);
1289: KSPGetConvergedReason(snes->ksp,&kspreason);
1290: if (kspreason < 0) {
1291: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
1292: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
1293: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
1294: break;
1295: }
1296: }
1298: VecScatterBegin(scat_act,Y_act,Y,INSERT_VALUES,SCATTER_REVERSE);
1299: VecScatterEnd(scat_act,Y_act,Y,INSERT_VALUES,SCATTER_REVERSE);
1300: VecScatterBegin(scat_inact,Y_inact,Y,INSERT_VALUES,SCATTER_REVERSE);
1301: VecScatterEnd(scat_inact,Y_inact,Y,INSERT_VALUES,SCATTER_REVERSE);
1303: VecDestroy(&F_inact);
1304: VecDestroy(&Y_act);
1305: VecDestroy(&Y_inact);
1306: VecScatterDestroy(&scat_act);
1307: VecScatterDestroy(&scat_inact);
1308: ISDestroy(&IS_act);
1309: if (!isequal) {
1310: ISDestroy(&vi->IS_inact_prev);
1311: ISDuplicate(IS_inact,&vi->IS_inact_prev);
1312: }
1313: ISDestroy(&IS_inact);
1314: MatDestroy(&jac_inact_inact);
1315: if (snes->jacobian != snes->jacobian_pre) {
1316: MatDestroy(&prejac_inact_inact);
1317: }
1318: KSPGetIterationNumber(snes->ksp,&lits);
1319: snes->linear_its += lits;
1320: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
1321: /*
1322: if (vi->precheckstep) {
1323: PetscBool changed_y = PETSC_FALSE;
1324: (*vi->precheckstep)(snes,X,Y,vi->precheck,&changed_y);
1325: }
1327: if (PetscLogPrintInfo){
1328: SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
1329: }
1330: */
1331: /* Compute a (scaled) negative update in the line search routine:
1332: Y <- X - lambda*Y
1333: and evaluate G = function(Y) (depends on the line search).
1334: */
1335: VecCopy(Y,snes->vec_sol_update);
1336: ynorm = 1; gnorm = fnorm;
1337: (*vi->LineSearch)(snes,vi->lsP,X,F,G,Y,W,fnorm,xnorm,&ynorm,&gnorm,&lssucceed);
1338: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",fnorm,gnorm,ynorm,(int)lssucceed);
1339: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
1340: if (snes->domainerror) {
1341: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1342: DMDestroyVI(snes->dm);
1343: return(0);
1344: }
1345: if (!lssucceed) {
1346: if (++snes->numFailures >= snes->maxFailures) {
1347: PetscBool ismin;
1348: snes->reason = SNES_DIVERGED_LINE_SEARCH;
1349: SNESVICheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);
1350: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
1351: break;
1352: }
1353: }
1354: /* Update function and solution vectors */
1355: fnorm = gnorm;
1356: VecCopy(G,F);
1357: VecCopy(W,X);
1358: /* Monitor convergence */
1359: PetscObjectTakeAccess(snes);
1360: snes->iter = i+1;
1361: snes->norm = fnorm;
1362: PetscObjectGrantAccess(snes);
1363: SNESLogConvHistory(snes,snes->norm,lits);
1364: SNESMonitor(snes,snes->iter,snes->norm);
1365: /* Test for convergence, xnorm = || X || */
1366: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
1367: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
1368: if (snes->reason) break;
1369: }
1370: DMDestroyVI(snes->dm);
1371: if (i == maxits) {
1372: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
1373: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
1374: }
1375: return(0);
1376: }
1380: PetscErrorCode SNESVISetRedundancyCheck(SNES snes,PetscErrorCode (*func)(SNES,IS,IS*,void*),void *ctx)
1381: {
1382: SNES_VI *vi = (SNES_VI*)snes->data;
1386: vi->checkredundancy = func;
1387: vi->ctxP = ctx;
1388: return(0);
1389: }
1391: #if defined(PETSC_HAVE_MATLAB_ENGINE)
1392: #include <engine.h>
1393: #include <mex.h>
1394: typedef struct {char *funcname; mxArray *ctx;} SNESMatlabContext;
1398: PetscErrorCode SNESVIRedundancyCheck_Matlab(SNES snes,IS is_act,IS* is_redact,void* ctx)
1399: {
1400: PetscErrorCode ierr;
1401: SNESMatlabContext *sctx = (SNESMatlabContext*)ctx;
1402: int nlhs = 1, nrhs = 5;
1403: mxArray *plhs[1], *prhs[5];
1404: long long int l1 = 0, l2 = 0, ls = 0;
1405: PetscInt *indices=PETSC_NULL;
1413: /* Create IS for reduced active set of size 0, its size and indices will
1414: bet set by the Matlab function */
1415: ISCreateGeneral(((PetscObject)snes)->comm,0,indices,PETSC_OWN_POINTER,is_redact);
1416: /* call Matlab function in ctx */
1417: PetscMemcpy(&ls,&snes,sizeof(snes));
1418: PetscMemcpy(&l1,&is_act,sizeof(is_act));
1419: PetscMemcpy(&l2,is_redact,sizeof(is_act));
1420: prhs[0] = mxCreateDoubleScalar((double)ls);
1421: prhs[1] = mxCreateDoubleScalar((double)l1);
1422: prhs[2] = mxCreateDoubleScalar((double)l2);
1423: prhs[3] = mxCreateString(sctx->funcname);
1424: prhs[4] = sctx->ctx;
1425: mexCallMATLAB(nlhs,plhs,nrhs,prhs,"PetscSNESVIRedundancyCheckInternal");
1426: mxGetScalar(plhs[0]);
1427: mxDestroyArray(prhs[0]);
1428: mxDestroyArray(prhs[1]);
1429: mxDestroyArray(prhs[2]);
1430: mxDestroyArray(prhs[3]);
1431: mxDestroyArray(plhs[0]);
1432: return(0);
1433: }
1437: PetscErrorCode SNESVISetRedundancyCheckMatlab(SNES snes,const char* func,mxArray* ctx)
1438: {
1439: PetscErrorCode ierr;
1440: SNESMatlabContext *sctx;
1443: /* currently sctx is memory bleed */
1444: PetscMalloc(sizeof(SNESMatlabContext),&sctx);
1445: PetscStrallocpy(func,&sctx->funcname);
1446: sctx->ctx = mxDuplicateArray(ctx);
1447: SNESVISetRedundancyCheck(snes,SNESVIRedundancyCheck_Matlab,sctx);
1448: return(0);
1449: }
1450:
1451: #endif
1454: /* Variational Inequality solver using augmented space method. It does the opposite of the
1455: reduced space method i.e. it identifies the active set variables and instead of discarding
1456: them it augments the original system by introducing additional equality
1457: constraint equations for active set variables. The user can optionally provide an IS for
1458: a subset of 'redundant' active set variables which will treated as free variables.
1459: Specific implementation for Allen-Cahn problem
1460: */
1463: PetscErrorCode SNESSolveVI_RSAUG(SNES snes)
1464: {
1465: SNES_VI *vi = (SNES_VI*)snes->data;
1466: PetscErrorCode ierr;
1467: PetscInt maxits,i,lits;
1468: PetscBool lssucceed;
1469: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
1470: PetscReal fnorm,gnorm,xnorm=0,ynorm;
1471: Vec Y,X,F,G,W;
1472: KSPConvergedReason kspreason;
1475: snes->numFailures = 0;
1476: snes->numLinearSolveFailures = 0;
1477: snes->reason = SNES_CONVERGED_ITERATING;
1479: maxits = snes->max_its; /* maximum number of iterations */
1480: X = snes->vec_sol; /* solution vector */
1481: F = snes->vec_func; /* residual vector */
1482: Y = snes->work[0]; /* work vectors */
1483: G = snes->work[1];
1484: W = snes->work[2];
1486: PetscObjectTakeAccess(snes);
1487: snes->iter = 0;
1488: snes->norm = 0.0;
1489: PetscObjectGrantAccess(snes);
1491: SNESVIProjectOntoBounds(snes,X);
1492: SNESComputeFunction(snes,X,F);
1493: if (snes->domainerror) {
1494: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1495: return(0);
1496: }
1497: SNESVIComputeInactiveSetFnorm(snes,F,X,&fnorm);
1498: VecNormBegin(X,NORM_2,&xnorm); /* xnorm <- ||x|| */
1499: VecNormEnd(X,NORM_2,&xnorm);
1500: if (PetscIsInfOrNanReal(fnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
1502: PetscObjectTakeAccess(snes);
1503: snes->norm = fnorm;
1504: PetscObjectGrantAccess(snes);
1505: SNESLogConvHistory(snes,fnorm,0);
1506: SNESMonitor(snes,0,fnorm);
1508: /* set parameter for default relative tolerance convergence test */
1509: snes->ttol = fnorm*snes->rtol;
1510: /* test convergence */
1511: (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
1512: if (snes->reason) return(0);
1514: for (i=0; i<maxits; i++) {
1515: IS IS_act,IS_inact; /* _act -> active set _inact -> inactive set */
1516: IS IS_redact; /* redundant active set */
1517: Mat J_aug,Jpre_aug;
1518: Vec F_aug,Y_aug;
1519: PetscInt nis_redact,nis_act;
1520: const PetscInt *idx_redact,*idx_act;
1521: PetscInt k;
1522: PetscInt *idx_actkept=PETSC_NULL,nkept=0; /* list of kept active set */
1523: PetscScalar *f,*f2;
1524: PetscBool isequal;
1525: PetscInt i1=0,j1=0;
1527: /* Call general purpose update function */
1528: if (snes->ops->update) {
1529: (*snes->ops->update)(snes, snes->iter);
1530: }
1531: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
1533: /* Create active and inactive index sets */
1534: SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&IS_inact);
1536: /* Get local active set size */
1537: ISGetLocalSize(IS_act,&nis_act);
1538: if (nis_act) {
1539: ISGetIndices(IS_act,&idx_act);
1540: IS_redact = PETSC_NULL;
1541: if(vi->checkredundancy) {
1542: (*vi->checkredundancy)(snes,IS_act,&IS_redact,vi->ctxP);
1543: }
1545: if(!IS_redact) {
1546: /* User called checkredundancy function but didn't create IS_redact because
1547: there were no redundant active set variables */
1548: /* Copy over all active set indices to the list */
1549: PetscMalloc(nis_act*sizeof(PetscInt),&idx_actkept);
1550: for(k=0;k < nis_act;k++) idx_actkept[k] = idx_act[k];
1551: nkept = nis_act;
1552: } else {
1553: ISGetLocalSize(IS_redact,&nis_redact);
1554: PetscMalloc((nis_act-nis_redact)*sizeof(PetscInt),&idx_actkept);
1556: /* Create reduced active set list */
1557: ISGetIndices(IS_act,&idx_act);
1558: ISGetIndices(IS_redact,&idx_redact);
1559: j1 = 0;nkept = 0;
1560: for(k=0;k<nis_act;k++) {
1561: if(j1 < nis_redact && idx_act[k] == idx_redact[j1]) j1++;
1562: else idx_actkept[nkept++] = idx_act[k];
1563: }
1564: ISRestoreIndices(IS_act,&idx_act);
1565: ISRestoreIndices(IS_redact,&idx_redact);
1567: ISDestroy(&IS_redact);
1568: }
1570: /* Create augmented F and Y */
1571: VecCreate(((PetscObject)snes)->comm,&F_aug);
1572: VecSetSizes(F_aug,F->map->n+nkept,PETSC_DECIDE);
1573: VecSetFromOptions(F_aug);
1574: VecDuplicate(F_aug,&Y_aug);
1575:
1576: /* Copy over F to F_aug in the first n locations */
1577: VecGetArray(F,&f);
1578: VecGetArray(F_aug,&f2);
1579: PetscMemcpy(f2,f,F->map->n*sizeof(PetscScalar));
1580: VecRestoreArray(F,&f);
1581: VecRestoreArray(F_aug,&f2);
1582:
1583: /* Create the augmented jacobian matrix */
1584: MatCreate(((PetscObject)snes)->comm,&J_aug);
1585: MatSetSizes(J_aug,X->map->n+nkept,X->map->n+nkept,PETSC_DECIDE,PETSC_DECIDE);
1586: MatSetFromOptions(J_aug);
1587:
1589: { /* local vars */
1590: /* Preallocate augmented matrix and set values in it...Doing only sequential case first*/
1591: PetscInt ncols;
1592: const PetscInt *cols;
1593: const PetscScalar *vals;
1594: PetscScalar value[2];
1595: PetscInt row,col[2];
1596: PetscInt *d_nnz;
1597: value[0] = 1.0; value[1] = 0.0;
1598: PetscMalloc((X->map->n+nkept)*sizeof(PetscInt),&d_nnz);
1599: PetscMemzero(d_nnz,(X->map->n+nkept)*sizeof(PetscInt));
1600: for(row=0;row<snes->jacobian->rmap->n;row++) {
1601: MatGetRow(snes->jacobian,row,&ncols,PETSC_NULL,PETSC_NULL);
1602: d_nnz[row] += ncols;
1603: MatRestoreRow(snes->jacobian,row,&ncols,PETSC_NULL,PETSC_NULL);
1604: }
1606: for(k=0;k<nkept;k++) {
1607: d_nnz[idx_actkept[k]] += 1;
1608: d_nnz[snes->jacobian->rmap->n+k] += 2;
1609: }
1610: MatSeqAIJSetPreallocation(J_aug,PETSC_NULL,d_nnz);
1611:
1612: PetscFree(d_nnz);
1614: /* Set the original jacobian matrix in J_aug */
1615: for(row=0;row<snes->jacobian->rmap->n;row++) {
1616: MatGetRow(snes->jacobian,row,&ncols,&cols,&vals);
1617: MatSetValues(J_aug,1,&row,ncols,cols,vals,INSERT_VALUES);
1618: MatRestoreRow(snes->jacobian,row,&ncols,&cols,&vals);
1619: }
1620: /* Add the augmented part */
1621: for(k=0;k<nkept;k++) {
1622: row = snes->jacobian->rmap->n+k;
1623: col[0] = idx_actkept[k]; col[1] = snes->jacobian->rmap->n+k;
1624: MatSetValues(J_aug,1,&row,1,col,value,INSERT_VALUES);
1625: MatSetValues(J_aug,1,&col[0],1,&row,&value[0],INSERT_VALUES);
1626: }
1627: MatAssemblyBegin(J_aug,MAT_FINAL_ASSEMBLY);
1628: MatAssemblyEnd(J_aug,MAT_FINAL_ASSEMBLY);
1629: /* Only considering prejac = jac for now */
1630: Jpre_aug = J_aug;
1631: } /* local vars*/
1632: ISRestoreIndices(IS_act,&idx_act);
1633: ISDestroy(&IS_act);
1634: } else {
1635: F_aug = F; J_aug = snes->jacobian; Y_aug = Y; Jpre_aug = snes->jacobian_pre;
1636: }
1638: ISEqual(vi->IS_inact_prev,IS_inact,&isequal);
1639: if (!isequal) {
1640: SNESVIResetPCandKSP(snes,J_aug,Jpre_aug);
1641: }
1642: KSPSetOperators(snes->ksp,J_aug,Jpre_aug,flg);
1643: KSPSetUp(snes->ksp);
1644: /* {
1645: PC pc;
1646: PetscBool flg;
1647: KSPGetPC(snes->ksp,&pc);
1648: PetscTypeCompare((PetscObject)pc,PCFIELDSPLIT,&flg);
1649: if (flg) {
1650: KSP *subksps;
1651: PCFieldSplitGetSubKSP(pc,PETSC_NULL,&subksps);
1652: KSPGetPC(subksps[0],&pc);
1653: PetscFree(subksps);
1654: PetscTypeCompare((PetscObject)pc,PCBJACOBI,&flg);
1655: if (flg) {
1656: PetscInt n,N = 101*101,j,cnts[3] = {0,0,0};
1657: const PetscInt *ii;
1659: ISGetSize(IS_inact,&n);
1660: ISGetIndices(IS_inact,&ii);
1661: for (j=0; j<n; j++) {
1662: if (ii[j] < N) cnts[0]++;
1663: else if (ii[j] < 2*N) cnts[1]++;
1664: else if (ii[j] < 3*N) cnts[2]++;
1665: }
1666: ISRestoreIndices(IS_inact,&ii);
1668: PCBJacobiSetTotalBlocks(pc,3,cnts);
1669: }
1670: }
1671: }
1672: */
1673: SNES_KSPSolve(snes,snes->ksp,F_aug,Y_aug);
1674: KSPGetConvergedReason(snes->ksp,&kspreason);
1675: if (kspreason < 0) {
1676: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
1677: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
1678: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
1679: break;
1680: }
1681: }
1683: if(nis_act) {
1684: PetscScalar *y1,*y2;
1685: VecGetArray(Y,&y1);
1686: VecGetArray(Y_aug,&y2);
1687: /* Copy over inactive Y_aug to Y */
1688: j1 = 0;
1689: for(i1=Y->map->rstart;i1 < Y->map->rend;i1++) {
1690: if(j1 < nkept && idx_actkept[j1] == i1) j1++;
1691: else y1[i1-Y->map->rstart] = y2[i1-Y->map->rstart];
1692: }
1693: VecRestoreArray(Y,&y1);
1694: VecRestoreArray(Y_aug,&y2);
1696: VecDestroy(&F_aug);
1697: VecDestroy(&Y_aug);
1698: MatDestroy(&J_aug);
1699: PetscFree(idx_actkept);
1700: }
1701:
1702: if (!isequal) {
1703: ISDestroy(&vi->IS_inact_prev);
1704: ISDuplicate(IS_inact,&vi->IS_inact_prev);
1705: }
1706: ISDestroy(&IS_inact);
1708: KSPGetIterationNumber(snes->ksp,&lits);
1709: snes->linear_its += lits;
1710: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
1711: /*
1712: if (vi->precheckstep) {
1713: PetscBool changed_y = PETSC_FALSE;
1714: (*vi->precheckstep)(snes,X,Y,vi->precheck,&changed_y);
1715: }
1717: if (PetscLogPrintInfo){
1718: SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
1719: }
1720: */
1721: /* Compute a (scaled) negative update in the line search routine:
1722: Y <- X - lambda*Y
1723: and evaluate G = function(Y) (depends on the line search).
1724: */
1725: VecCopy(Y,snes->vec_sol_update);
1726: ynorm = 1; gnorm = fnorm;
1727: (*vi->LineSearch)(snes,vi->lsP,X,F,G,Y,W,fnorm,xnorm,&ynorm,&gnorm,&lssucceed);
1728: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",fnorm,gnorm,ynorm,(int)lssucceed);
1729: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
1730: if (snes->domainerror) {
1731: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
1732: return(0);
1733: }
1734: if (!lssucceed) {
1735: if (++snes->numFailures >= snes->maxFailures) {
1736: PetscBool ismin;
1737: snes->reason = SNES_DIVERGED_LINE_SEARCH;
1738: SNESVICheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);
1739: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
1740: break;
1741: }
1742: }
1743: /* Update function and solution vectors */
1744: fnorm = gnorm;
1745: VecCopy(G,F);
1746: VecCopy(W,X);
1747: /* Monitor convergence */
1748: PetscObjectTakeAccess(snes);
1749: snes->iter = i+1;
1750: snes->norm = fnorm;
1751: PetscObjectGrantAccess(snes);
1752: SNESLogConvHistory(snes,snes->norm,lits);
1753: SNESMonitor(snes,snes->iter,snes->norm);
1754: /* Test for convergence, xnorm = || X || */
1755: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
1756: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
1757: if (snes->reason) break;
1758: }
1759: if (i == maxits) {
1760: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
1761: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
1762: }
1763: return(0);
1764: }
1766: /* -------------------------------------------------------------------------- */
1767: /*
1768: SNESSetUp_VI - Sets up the internal data structures for the later use
1769: of the SNESVI nonlinear solver.
1771: Input Parameter:
1772: . snes - the SNES context
1773: . x - the solution vector
1775: Application Interface Routine: SNESSetUp()
1777: Notes:
1778: For basic use of the SNES solvers, the user need not explicitly call
1779: SNESSetUp(), since these actions will automatically occur during
1780: the call to SNESSolve().
1781: */
1784: PetscErrorCode SNESSetUp_VI(SNES snes)
1785: {
1787: SNES_VI *vi = (SNES_VI*) snes->data;
1788: PetscInt i_start[3],i_end[3];
1792: SNESDefaultGetWork(snes,3);
1794: if (vi->computevariablebounds) {
1795: if (!vi->xl) {VecDuplicate(snes->vec_sol,&vi->xl);}
1796: if (!vi->xu) {VecDuplicate(snes->vec_sol,&vi->xu);}
1797: (*vi->computevariablebounds)(snes,vi->xl,vi->xu);
1798: } else if (!vi->xl && !vi->xu) {
1799: /* If the lower and upper bound on variables are not set, set it to -Inf and Inf */
1800: VecDuplicate(snes->vec_sol, &vi->xl);
1801: VecSet(vi->xl,SNES_VI_NINF);
1802: VecDuplicate(snes->vec_sol, &vi->xu);
1803: VecSet(vi->xu,SNES_VI_INF);
1804: } else {
1805: /* Check if lower bound, upper bound and solution vector distribution across the processors is identical */
1806: VecGetOwnershipRange(snes->vec_sol,i_start,i_end);
1807: VecGetOwnershipRange(vi->xl,i_start+1,i_end+1);
1808: VecGetOwnershipRange(vi->xu,i_start+2,i_end+2);
1809: 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]))
1810: 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.");
1811: }
1812: if (snes->ops->solve != SNESSolveVI_SS) {
1813: /* Set up previous active index set for the first snes solve
1814: vi->IS_inact_prev = 0,1,2,....N */
1815: PetscInt *indices;
1816: PetscInt i,n,rstart,rend;
1818: VecGetOwnershipRange(snes->vec_sol,&rstart,&rend);
1819: VecGetLocalSize(snes->vec_sol,&n);
1820: PetscMalloc(n*sizeof(PetscInt),&indices);
1821: for(i=0;i < n; i++) indices[i] = rstart + i;
1822: ISCreateGeneral(((PetscObject)snes)->comm,n,indices,PETSC_OWN_POINTER,&vi->IS_inact_prev);
1823: }
1825: if (snes->ops->solve == SNESSolveVI_SS) {
1826: VecDuplicate(snes->vec_sol, &vi->dpsi);
1827: VecDuplicate(snes->vec_sol, &vi->phi);
1828: VecDuplicate(snes->vec_sol, &vi->Da);
1829: VecDuplicate(snes->vec_sol, &vi->Db);
1830: VecDuplicate(snes->vec_sol, &vi->z);
1831: VecDuplicate(snes->vec_sol, &vi->t);
1832: }
1833: return(0);
1834: }
1835: /* -------------------------------------------------------------------------- */
1838: PetscErrorCode SNESReset_VI(SNES snes)
1839: {
1840: SNES_VI *vi = (SNES_VI*) snes->data;
1844: VecDestroy(&vi->xl);
1845: VecDestroy(&vi->xu);
1846: if (snes->ops->solve != SNESSolveVI_SS) {
1847: ISDestroy(&vi->IS_inact_prev);
1848: }
1849: return(0);
1850: }
1852: /*
1853: SNESDestroy_VI - Destroys the private SNES_VI context that was created
1854: with SNESCreate_VI().
1856: Input Parameter:
1857: . snes - the SNES context
1859: Application Interface Routine: SNESDestroy()
1860: */
1863: PetscErrorCode SNESDestroy_VI(SNES snes)
1864: {
1865: SNES_VI *vi = (SNES_VI*) snes->data;
1870: if (snes->ops->solve == SNESSolveVI_SS) {
1871: /* clear vectors */
1872: VecDestroy(&vi->dpsi);
1873: VecDestroy(&vi->phi);
1874: VecDestroy(&vi->Da);
1875: VecDestroy(&vi->Db);
1876: VecDestroy(&vi->z);
1877: VecDestroy(&vi->t);
1878: }
1879:
1880: PetscViewerDestroy(&vi->lsmonitor);
1881: PetscFree(snes->data);
1883: /* clear composed functions */
1884: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","",PETSC_NULL);
1885: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetMonitor_C","",PETSC_NULL);
1886: return(0);
1887: }
1889: /* -------------------------------------------------------------------------- */
1893: /*
1894: This routine does not actually do a line search but it takes a full newton
1895: step while ensuring that the new iterates remain within the constraints.
1896:
1897: */
1898: 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)
1899: {
1901: SNES_VI *vi = (SNES_VI*)snes->data;
1902: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
1905: *flag = PETSC_TRUE;
1906: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
1907: VecNorm(y,NORM_2,ynorm); /* ynorm = || y || */
1908: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1909: SNESVIProjectOntoBounds(snes,w);
1910: if (vi->postcheckstep) {
1911: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
1912: }
1913: if (changed_y) {
1914: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1915: SNESVIProjectOntoBounds(snes,w);
1916: }
1917: SNESVIProjectOntoBounds(snes,w);
1918: SNESComputeFunction(snes,w,g);
1919: if (!snes->domainerror) {
1920: if (snes->ops->solve != SNESSolveVI_SS) {
1921: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
1922: } else {
1923: VecNorm(g,NORM_2,gnorm); /* gnorm = || g || */
1924: }
1925: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
1926: }
1927: if (vi->lsmonitor) {
1928: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1929: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Using full step: fnorm %g gnorm %g\n",(double)fnorm,(double)*gnorm);
1930: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1931: }
1932: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
1933: return(0);
1934: }
1936: /* -------------------------------------------------------------------------- */
1940: /*
1941: This routine is a copy of SNESLineSearchNoNorms in snes/impls/ls/ls.c
1942: */
1943: 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)
1944: {
1946: SNES_VI *vi = (SNES_VI*)snes->data;
1947: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
1950: *flag = PETSC_TRUE;
1951: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
1952: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1953: SNESVIProjectOntoBounds(snes,w);
1954: if (vi->postcheckstep) {
1955: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
1956: }
1957: if (changed_y) {
1958: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
1959: SNESVIProjectOntoBounds(snes,w);
1960: }
1961:
1962: /* don't evaluate function the last time through */
1963: if (snes->iter < snes->max_its-1) {
1964: SNESComputeFunction(snes,w,g);
1965: }
1966: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
1967: return(0);
1968: }
1970: /* -------------------------------------------------------------------------- */
1973: /*
1974: This routine implements a cubic line search while doing a projection on the variable bounds
1975: */
1976: 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)
1977: {
1978: PetscReal initslope,lambdaprev,gnormprev,a,b,d,t1,t2,rellength;
1979: PetscReal minlambda,lambda,lambdatemp;
1980: #if defined(PETSC_USE_COMPLEX)
1981: PetscScalar cinitslope;
1982: #endif
1984: PetscInt count;
1985: SNES_VI *vi = (SNES_VI*)snes->data;
1986: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
1987: MPI_Comm comm;
1990: PetscObjectGetComm((PetscObject)snes,&comm);
1991: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
1992: *flag = PETSC_TRUE;
1994: VecNorm(y,NORM_2,ynorm);
1995: if (*ynorm == 0.0) {
1996: if (vi->lsmonitor) {
1997: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
1998: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Initial direction and size is 0\n");
1999: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2000: }
2001: *gnorm = fnorm;
2002: VecCopy(x,w);
2003: VecCopy(f,g);
2004: *flag = PETSC_FALSE;
2005: goto theend1;
2006: }
2007: if (*ynorm > vi->maxstep) { /* Step too big, so scale back */
2008: if (vi->lsmonitor) {
2009: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2010: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Scaling step by %g old ynorm %g\n",(double)vi->maxstep/(*ynorm),(double)*ynorm);
2011: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2012: }
2013: VecScale(y,vi->maxstep/(*ynorm));
2014: *ynorm = vi->maxstep;
2015: }
2016: VecMaxPointwiseDivide(y,x,&rellength);
2017: minlambda = vi->minlambda/rellength;
2018: MatMult(snes->jacobian,y,w);
2019: #if defined(PETSC_USE_COMPLEX)
2020: VecDot(f,w,&cinitslope);
2021: initslope = PetscRealPart(cinitslope);
2022: #else
2023: VecDot(f,w,&initslope);
2024: #endif
2025: if (initslope > 0.0) initslope = -initslope;
2026: if (initslope == 0.0) initslope = -1.0;
2028: VecWAXPY(w,-1.0,y,x);
2029: SNESVIProjectOntoBounds(snes,w);
2030: if (snes->nfuncs >= snes->max_funcs) {
2031: PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");
2032: *flag = PETSC_FALSE;
2033: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2034: goto theend1;
2035: }
2036: SNESComputeFunction(snes,w,g);
2037: if (snes->ops->solve != SNESSolveVI_SS) {
2038: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2039: } else {
2040: VecNorm(g,NORM_2,gnorm);
2041: }
2042: if (snes->domainerror) {
2043: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2044: return(0);
2045: }
2046: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2047: PetscInfo4(snes,"Initial fnorm %g gnorm %g alpha %g initslope %g\n",(double)fnorm,(double)*gnorm,(double)vi->alpha,(double)initslope);
2048: if ((*gnorm)*(*gnorm) <= (1.0 - vi->alpha)*fnorm*fnorm ) { /* Sufficient reduction */
2049: if (vi->lsmonitor) {
2050: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2051: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Using full step: fnorm %g gnorm %g\n",(double)fnorm,(double)*gnorm);
2052: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2053: }
2054: goto theend1;
2055: }
2057: /* Fit points with quadratic */
2058: lambda = 1.0;
2059: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
2060: lambdaprev = lambda;
2061: gnormprev = *gnorm;
2062: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
2063: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
2064: else lambda = lambdatemp;
2066: VecWAXPY(w,-lambda,y,x);
2067: SNESVIProjectOntoBounds(snes,w);
2068: if (snes->nfuncs >= snes->max_funcs) {
2069: PetscInfo1(snes,"Exceeded maximum function evaluations, while attempting quadratic backtracking! %D \n",snes->nfuncs);
2070: *flag = PETSC_FALSE;
2071: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2072: goto theend1;
2073: }
2074: SNESComputeFunction(snes,w,g);
2075: if (snes->ops->solve != SNESSolveVI_SS) {
2076: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2077: } else {
2078: VecNorm(g,NORM_2,gnorm);
2079: }
2080: if (snes->domainerror) {
2081: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2082: return(0);
2083: }
2084: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2085: if (vi->lsmonitor) {
2086: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2087: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: gnorm after quadratic fit %g\n",(double)*gnorm);
2088: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2089: }
2090: if ((*gnorm)*(*gnorm) < (1.0 - vi->alpha)*fnorm*fnorm ) { /* sufficient reduction */
2091: if (vi->lsmonitor) {
2092: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2093: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Quadratically determined step, lambda=%18.16e\n",lambda);
2094: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2095: }
2096: goto theend1;
2097: }
2099: /* Fit points with cubic */
2100: count = 1;
2101: while (PETSC_TRUE) {
2102: if (lambda <= minlambda) {
2103: if (vi->lsmonitor) {
2104: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2105: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: unable to find good step length! After %D tries \n",count);
2106: 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);
2107: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2108: }
2109: *flag = PETSC_FALSE;
2110: break;
2111: }
2112: t1 = .5*((*gnorm)*(*gnorm) - fnorm*fnorm) - lambda*initslope;
2113: t2 = .5*(gnormprev*gnormprev - fnorm*fnorm) - lambdaprev*initslope;
2114: a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2115: b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
2116: d = b*b - 3*a*initslope;
2117: if (d < 0.0) d = 0.0;
2118: if (a == 0.0) {
2119: lambdatemp = -initslope/(2.0*b);
2120: } else {
2121: lambdatemp = (-b + sqrt(d))/(3.0*a);
2122: }
2123: lambdaprev = lambda;
2124: gnormprev = *gnorm;
2125: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
2126: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
2127: else lambda = lambdatemp;
2128: VecWAXPY(w,-lambda,y,x);
2129: SNESVIProjectOntoBounds(snes,w);
2130: if (snes->nfuncs >= snes->max_funcs) {
2131: PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);
2132: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
2133: *flag = PETSC_FALSE;
2134: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2135: break;
2136: }
2137: SNESComputeFunction(snes,w,g);
2138: if (snes->ops->solve != SNESSolveVI_SS) {
2139: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2140: } else {
2141: VecNorm(g,NORM_2,gnorm);
2142: }
2143: if (snes->domainerror) {
2144: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2145: return(0);
2146: }
2147: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2148: if ((*gnorm)*(*gnorm) < (1.0 - vi->alpha)*fnorm*fnorm) { /* is reduction enough? */
2149: if (vi->lsmonitor) {
2150: PetscPrintf(comm," Line search: Cubically determined step, current gnorm %g lambda=%18.16e\n",(double)*gnorm,(double)lambda);
2151: }
2152: break;
2153: } else {
2154: if (vi->lsmonitor) {
2155: PetscPrintf(comm," Line search: Cubic step no good, shrinking lambda, current gnorm %g lambda=%18.16e\n",(double)*gnorm,(double)lambda);
2156: }
2157: }
2158: count++;
2159: }
2160: theend1:
2161: /* Optional user-defined check for line search step validity */
2162: if (vi->postcheckstep && *flag) {
2163: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
2164: if (changed_y) {
2165: VecWAXPY(w,-1.0,y,x);
2166: SNESVIProjectOntoBounds(snes,w);
2167: }
2168: if (changed_y || changed_w) { /* recompute the function if the step has changed */
2169: SNESComputeFunction(snes,w,g);
2170: if (snes->ops->solve != SNESSolveVI_SS) {
2171: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2172: } else {
2173: VecNorm(g,NORM_2,gnorm);
2174: }
2175: if (snes->domainerror) {
2176: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2177: return(0);
2178: }
2179: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2180: VecNormBegin(y,NORM_2,ynorm);
2181: VecNormEnd(y,NORM_2,ynorm);
2182: }
2183: }
2184: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2185: return(0);
2186: }
2190: /*
2191: This routine does a quadratic line search while keeping the iterates within the variable bounds
2192: */
2193: 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)
2194: {
2195: /*
2196: Note that for line search purposes we work with with the related
2197: minimization problem:
2198: min z(x): R^n -> R,
2199: where z(x) = .5 * fnorm*fnorm,and fnorm = || f ||_2.
2200: */
2201: PetscReal initslope,minlambda,lambda,lambdatemp,rellength;
2202: #if defined(PETSC_USE_COMPLEX)
2203: PetscScalar cinitslope;
2204: #endif
2206: PetscInt count;
2207: SNES_VI *vi = (SNES_VI*)snes->data;
2208: PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
2211: PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);
2212: *flag = PETSC_TRUE;
2214: VecNorm(y,NORM_2,ynorm);
2215: if (*ynorm == 0.0) {
2216: if (vi->lsmonitor) {
2217: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2218: PetscViewerASCIIPrintf(vi->lsmonitor,"Line search: Direction and size is 0\n");
2219: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2220: }
2221: *gnorm = fnorm;
2222: VecCopy(x,w);
2223: VecCopy(f,g);
2224: *flag = PETSC_FALSE;
2225: goto theend2;
2226: }
2227: if (*ynorm > vi->maxstep) { /* Step too big, so scale back */
2228: VecScale(y,vi->maxstep/(*ynorm));
2229: *ynorm = vi->maxstep;
2230: }
2231: VecMaxPointwiseDivide(y,x,&rellength);
2232: minlambda = vi->minlambda/rellength;
2233: MatMult(snes->jacobian,y,w);
2234: #if defined(PETSC_USE_COMPLEX)
2235: VecDot(f,w,&cinitslope);
2236: initslope = PetscRealPart(cinitslope);
2237: #else
2238: VecDot(f,w,&initslope);
2239: #endif
2240: if (initslope > 0.0) initslope = -initslope;
2241: if (initslope == 0.0) initslope = -1.0;
2242: PetscInfo1(snes,"Initslope %G \n",initslope);
2244: VecWAXPY(w,-1.0,y,x);
2245: SNESVIProjectOntoBounds(snes,w);
2246: if (snes->nfuncs >= snes->max_funcs) {
2247: PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");
2248: *flag = PETSC_FALSE;
2249: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2250: goto theend2;
2251: }
2252: SNESComputeFunction(snes,w,g);
2253: if (snes->ops->solve != SNESSolveVI_SS) {
2254: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2255: } else {
2256: VecNorm(g,NORM_2,gnorm);
2257: }
2258: if (snes->domainerror) {
2259: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2260: return(0);
2261: }
2262: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2263: if ((*gnorm)*(*gnorm) <= (1.0 - vi->alpha)*fnorm*fnorm) { /* Sufficient reduction */
2264: if (vi->lsmonitor) {
2265: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2266: PetscViewerASCIIPrintf(vi->lsmonitor," Line search: Using full step: fnorm %G gnorm %G\n",fnorm,*gnorm);
2267: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2268: }
2269: goto theend2;
2270: }
2272: /* Fit points with quadratic */
2273: lambda = 1.0;
2274: count = 1;
2275: while (PETSC_TRUE) {
2276: if (lambda <= minlambda) { /* bad luck; use full step */
2277: if (vi->lsmonitor) {
2278: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2279: PetscViewerASCIIPrintf(vi->lsmonitor,"Line search: Unable to find good step length! %D \n",count);
2280: PetscViewerASCIIPrintf(vi->lsmonitor,"Line search: fnorm=%G, gnorm=%G, ynorm=%G, lambda=%G, initial slope=%G\n",fnorm,*gnorm,*ynorm,lambda,initslope);
2281: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2282: }
2283: VecCopy(x,w);
2284: *flag = PETSC_FALSE;
2285: break;
2286: }
2287: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
2288: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
2289: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
2290: else lambda = lambdatemp;
2291:
2292: VecWAXPY(w,-lambda,y,x);
2293: SNESVIProjectOntoBounds(snes,w);
2294: if (snes->nfuncs >= snes->max_funcs) {
2295: PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);
2296: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
2297: *flag = PETSC_FALSE;
2298: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
2299: break;
2300: }
2301: SNESComputeFunction(snes,w,g);
2302: if (snes->domainerror) {
2303: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2304: return(0);
2305: }
2306: if (snes->ops->solve != SNESSolveVI_SS) {
2307: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2308: } else {
2309: VecNorm(g,NORM_2,gnorm);
2310: }
2311: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2312: if ((*gnorm)*(*gnorm) < (1.0 - vi->alpha)*fnorm*fnorm) { /* sufficient reduction */
2313: if (vi->lsmonitor) {
2314: PetscViewerASCIIAddTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2315: PetscViewerASCIIPrintf(vi->lsmonitor," Line Search: Quadratically determined step, lambda=%G\n",lambda);
2316: PetscViewerASCIISubtractTab(vi->lsmonitor,((PetscObject)snes)->tablevel);
2317: }
2318: break;
2319: }
2320: count++;
2321: }
2322: theend2:
2323: /* Optional user-defined check for line search step validity */
2324: if (vi->postcheckstep) {
2325: (*vi->postcheckstep)(snes,x,y,w,vi->postcheck,&changed_y,&changed_w);
2326: if (changed_y) {
2327: VecWAXPY(w,-1.0,y,x);
2328: SNESVIProjectOntoBounds(snes,w);
2329: }
2330: if (changed_y || changed_w) { /* recompute the function if the step has changed */
2331: SNESComputeFunction(snes,w,g);
2332: if (snes->domainerror) {
2333: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2334: return(0);
2335: }
2336: if (snes->ops->solve != SNESSolveVI_SS) {
2337: SNESVIComputeInactiveSetFnorm(snes,g,w,gnorm);
2338: } else {
2339: VecNorm(g,NORM_2,gnorm);
2340: }
2342: VecNormBegin(y,NORM_2,ynorm);
2343: VecNormEnd(y,NORM_2,ynorm);
2344: if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
2345: }
2346: }
2347: PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);
2348: return(0);
2349: }
2352: /* -------------------------------------------------------------------------- */
2356: PetscErrorCode SNESLineSearchSet_VI(SNES snes,FCN2 func,void *lsctx)
2357: {
2359: ((SNES_VI *)(snes->data))->LineSearch = func;
2360: ((SNES_VI *)(snes->data))->lsP = lsctx;
2361: return(0);
2362: }
2365: /* -------------------------------------------------------------------------- */
2369: PetscErrorCode SNESLineSearchSetMonitor_VI(SNES snes,PetscBool flg)
2370: {
2371: SNES_VI *vi = (SNES_VI*)snes->data;
2375: if (flg && !vi->lsmonitor) {
2376: PetscViewerASCIIOpen(((PetscObject)snes)->comm,"stdout",&vi->lsmonitor);
2377: } else if (!flg && vi->lsmonitor) {
2378: PetscViewerDestroy(&vi->lsmonitor);
2379: }
2380: return(0);
2381: }
2384: /*
2385: SNESView_VI - Prints info from the SNESVI data structure.
2387: Input Parameters:
2388: . SNES - the SNES context
2389: . viewer - visualization context
2391: Application Interface Routine: SNESView()
2392: */
2395: static PetscErrorCode SNESView_VI(SNES snes,PetscViewer viewer)
2396: {
2397: SNES_VI *vi = (SNES_VI *)snes->data;
2398: const char *cstr,*tstr;
2400: PetscBool iascii;
2403: PetscTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
2404: if (iascii) {
2405: if (vi->LineSearch == SNESLineSearchNo_VI) cstr = "SNESLineSearchNo";
2406: else if (vi->LineSearch == SNESLineSearchQuadratic_VI) cstr = "SNESLineSearchQuadratic";
2407: else if (vi->LineSearch == SNESLineSearchCubic_VI) cstr = "SNESLineSearchCubic";
2408: else cstr = "unknown";
2409: if (snes->ops->solve == SNESSolveVI_SS) tstr = "Semismooth";
2410: else if (snes->ops->solve == SNESSolveVI_RS) tstr = "Reduced Space";
2411: else if (snes->ops->solve == SNESSolveVI_RSAUG) tstr = "Reduced space with augmented variables";
2412: else tstr = "unknown";
2413: PetscViewerASCIIPrintf(viewer," VI algorithm: %s\n",tstr);
2414: PetscViewerASCIIPrintf(viewer," line search variant: %s\n",cstr);
2415: PetscViewerASCIIPrintf(viewer," alpha=%G, maxstep=%G, minlambda=%G\n",vi->alpha,vi->maxstep,vi->minlambda);
2416: }
2417: return(0);
2418: }
2422: /*@
2423: SNESVISetVariableBounds - Sets the lower and upper bounds for the solution vector. xl <= x <= xu.
2425: Input Parameters:
2426: . snes - the SNES context.
2427: . xl - lower bound.
2428: . xu - upper bound.
2430: Notes:
2431: If this routine is not called then the lower and upper bounds are set to
2432: SNES_VI_INF and SNES_VI_NINF respectively during SNESSetUp().
2434: Level: advanced
2436: @*/
2437: PetscErrorCode SNESVISetVariableBounds(SNES snes, Vec xl, Vec xu)
2438: {
2439: SNES_VI *vi;
2440: PetscErrorCode ierr;
2441: const PetscScalar *xxl,*xxu;
2442: PetscInt i,n, cnt = 0;
2448: if (!snes->vec_func) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Must call SNESSetFunction() first");
2449: 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);
2450: 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);
2451: SNESSetType(snes,SNESVI);
2452: vi = (SNES_VI*)snes->data;
2453: PetscObjectReference((PetscObject)xl);
2454: PetscObjectReference((PetscObject)xu);
2455: VecDestroy(&vi->xl);
2456: VecDestroy(&vi->xu);
2457: vi->xl = xl;
2458: vi->xu = xu;
2459: VecGetLocalSize(xl,&n);
2460: VecGetArrayRead(xl,&xxl);
2461: VecGetArrayRead(xu,&xxu);
2462: for (i=0; i<n; i++) {
2463: cnt += ((xxl[i] != SNES_VI_NINF) || (xxu[i] != SNES_VI_INF));
2464: }
2465: MPI_Allreduce(&cnt,&vi->ntruebounds,1,MPIU_INT,MPI_SUM,((PetscObject)snes)->comm);
2466: VecRestoreArrayRead(xl,&xxl);
2467: VecRestoreArrayRead(xu,&xxu);
2468: return(0);
2469: }
2471: /* -------------------------------------------------------------------------- */
2472: /*
2473: SNESSetFromOptions_VI - Sets various parameters for the SNESVI method.
2475: Input Parameter:
2476: . snes - the SNES context
2478: Application Interface Routine: SNESSetFromOptions()
2479: */
2482: static PetscErrorCode SNESSetFromOptions_VI(SNES snes)
2483: {
2484: SNES_VI *vi = (SNES_VI *)snes->data;
2485: const char *lses[] = {"basic","basicnonorms","quadratic","cubic"};
2486: const char *vies[] = {"ss","rs","rsaug"};
2488: PetscInt indx;
2489: PetscBool flg,set,flg2;
2492: PetscOptionsHead("SNES semismooth method options");
2493: PetscOptionsBool("-snes_vi_monitor","Monitor all non-active variables","None",PETSC_FALSE,&flg,0);
2494: if (flg) {
2495: SNESMonitorSet(snes,SNESMonitorVI,0,0);
2496: }
2497: PetscOptionsReal("-snes_ls_alpha","Function norm must decrease by","None",vi->alpha,&vi->alpha,0);
2498: PetscOptionsReal("-snes_ls_maxstep","Step must be less than","None",vi->maxstep,&vi->maxstep,0);
2499: PetscOptionsReal("-snes_ls_minlambda","Minimum lambda allowed","None",vi->minlambda,&vi->minlambda,0);
2500: PetscOptionsReal("-snes_vi_const_tol","constraint tolerance","None",vi->const_tol,&vi->const_tol,0);
2501: PetscOptionsBool("-snes_ls_monitor","Print progress of line searches","SNESLineSearchSetMonitor",vi->lsmonitor ? PETSC_TRUE : PETSC_FALSE,&flg,&set);
2502: if (set) {SNESLineSearchSetMonitor(snes,flg);}
2503: PetscOptionsEList("-snes_vi_type","Semismooth algorithm used","",vies,3,"rs",&indx,&flg2);
2504: if (flg2) {
2505: switch (indx) {
2506: case 0:
2507: snes->ops->solve = SNESSolveVI_SS;
2508: break;
2509: case 1:
2510: snes->ops->solve = SNESSolveVI_RS;
2511: break;
2512: case 2:
2513: snes->ops->solve = SNESSolveVI_RSAUG;
2514: }
2515: }
2516: PetscOptionsEList("-snes_ls","Line search used","SNESLineSearchSet",lses,4,"cubic",&indx,&flg);
2517: if (flg) {
2518: switch (indx) {
2519: case 0:
2520: SNESLineSearchSet(snes,SNESLineSearchNo_VI,PETSC_NULL);
2521: break;
2522: case 1:
2523: SNESLineSearchSet(snes,SNESLineSearchNoNorms_VI,PETSC_NULL);
2524: break;
2525: case 2:
2526: SNESLineSearchSet(snes,SNESLineSearchQuadratic_VI,PETSC_NULL);
2527: break;
2528: case 3:
2529: SNESLineSearchSet(snes,SNESLineSearchCubic_VI,PETSC_NULL);
2530: break;
2531: }
2532: }
2533: PetscOptionsBool("-snes_vi_ignore_function_sign","Ignore the sign of function for active constraints","None",vi->ignorefunctionsign,&vi->ignorefunctionsign,PETSC_NULL);
2534: PetscOptionsTail();
2535: return(0);
2536: }
2537: /* -------------------------------------------------------------------------- */
2538: /*MC
2539: SNESVI - Various solvers for variational inequalities based on Newton's method
2541: Options Database:
2542: + -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
2543: additional variables that enforce the constraints
2544: - -snes_vi_monitor - prints the number of active constraints at each iteration.
2547: Level: beginner
2549: .seealso: SNESCreate(), SNES, SNESSetType(), SNESTR, SNESLineSearchSet(),
2550: SNESLineSearchSetPostCheck(), SNESLineSearchNo(), SNESLineSearchCubic(), SNESLineSearchQuadratic(),
2551: SNESLineSearchSet(), SNESLineSearchNoNorms(), SNESLineSearchSetPreCheck(), SNESLineSearchSetParams(), SNESLineSearchGetParams()
2553: M*/
2557: PetscErrorCode SNESCreate_VI(SNES snes)
2558: {
2560: SNES_VI *vi;
2563: snes->ops->reset = SNESReset_VI;
2564: snes->ops->setup = SNESSetUp_VI;
2565: snes->ops->solve = SNESSolveVI_RS;
2566: snes->ops->destroy = SNESDestroy_VI;
2567: snes->ops->setfromoptions = SNESSetFromOptions_VI;
2568: snes->ops->view = SNESView_VI;
2569: snes->ops->converged = SNESDefaultConverged_VI;
2571: PetscNewLog(snes,SNES_VI,&vi);
2572: snes->data = (void*)vi;
2573: vi->alpha = 1.e-4;
2574: vi->maxstep = 1.e8;
2575: vi->minlambda = 1.e-12;
2576: vi->LineSearch = SNESLineSearchCubic_VI;
2577: vi->lsP = PETSC_NULL;
2578: vi->postcheckstep = PETSC_NULL;
2579: vi->postcheck = PETSC_NULL;
2580: vi->precheckstep = PETSC_NULL;
2581: vi->precheck = PETSC_NULL;
2582: vi->const_tol = 2.2204460492503131e-16;
2583: vi->checkredundancy = PETSC_NULL;
2585: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetMonitor_C","SNESLineSearchSetMonitor_VI",SNESLineSearchSetMonitor_VI);
2586: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","SNESLineSearchSet_VI",SNESLineSearchSet_VI);
2588: return(0);
2589: }
2594: /*
2595: SNESVIGetInactiveSet - Gets the global indices for the inactive set variables (these correspond to the degrees of freedom the linear
2596: system is solved on)
2598: Input parameter
2599: . snes - the SNES context
2601: Output parameter
2602: . ISact - active set index set
2604: */
2605: PetscErrorCode SNESVIGetInactiveSet(SNES snes,IS* inact)
2606: {
2607: SNES_VI *vi = (SNES_VI*)snes->data;
2609: *inact = vi->IS_inact_prev;
2610: return(0);
2611: }