Actual source code: snesmfj2.c
2: #include src/snes/snesimpl.h
4: EXTERN PetscErrorCode DiffParameterCreate_More(SNES,Vec,void**);
5: EXTERN PetscErrorCode DiffParameterCompute_More(SNES,void*,Vec,Vec,PetscReal*,PetscReal*);
6: EXTERN PetscErrorCode DiffParameterDestroy_More(void*);
8: typedef struct { /* default context for matrix-free SNES */
9: SNES snes; /* SNES context */
10: Vec w; /* work vector */
11: MatNullSpace sp; /* null space context */
12: PetscReal error_rel; /* square root of relative error in computing function */
13: PetscReal umin; /* minimum allowable u'a value relative to |u|_1 */
14: PetscTruth jorge; /* flag indicating use of Jorge's method for determining
15: the differencing parameter */
16: PetscReal h; /* differencing parameter */
17: PetscTruth need_h; /* flag indicating whether we must compute h */
18: PetscTruth need_err; /* flag indicating whether we must currently compute error_rel */
19: PetscTruth compute_err; /* flag indicating whether we must ever compute error_rel */
20: PetscInt compute_err_iter; /* last iter where we've computer error_rel */
21: PetscInt compute_err_freq; /* frequency of computing error_rel */
22: void *data; /* implementation-specific data */
23: } MFCtx_Private;
27: PetscErrorCode SNESMatrixFreeDestroy2_Private(Mat mat)
28: {
30: MFCtx_Private *ctx;
33: MatShellGetContext(mat,(void **)&ctx);
34: VecDestroy(ctx->w);
35: if (ctx->sp) {MatNullSpaceDestroy(ctx->sp);}
36: if (ctx->jorge || ctx->compute_err) {DiffParameterDestroy_More(ctx->data);}
37: PetscFree(ctx);
38: return(0);
39: }
43: /*
44: SNESMatrixFreeView2_Private - Views matrix-free parameters.
45: */
46: PetscErrorCode SNESMatrixFreeView2_Private(Mat J,PetscViewer viewer)
47: {
49: MFCtx_Private *ctx;
50: PetscTruth iascii;
53: MatShellGetContext(J,(void **)&ctx);
54: PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&iascii);
55: if (iascii) {
56: PetscViewerASCIIPrintf(viewer," SNES matrix-free approximation:\n");
57: if (ctx->jorge) {
58: PetscViewerASCIIPrintf(viewer," using Jorge's method of determining differencing parameter\n");
59: }
60: PetscViewerASCIIPrintf(viewer," err=%g (relative error in function evaluation)\n",ctx->error_rel);
61: PetscViewerASCIIPrintf(viewer," umin=%g (minimum iterate parameter)\n",ctx->umin);
62: if (ctx->compute_err) {
63: PetscViewerASCIIPrintf(viewer," freq_err=%D (frequency for computing err)\n",ctx->compute_err_freq);
64: }
65: } else {
66: SETERRQ1(PETSC_ERR_SUP,"Viewer type %s not supported by SNES matrix free Jorge",((PetscObject)viewer)->type_name);
67: }
68: return(0);
69: }
73: /*
74: SNESMatrixFreeMult2_Private - Default matrix-free form for Jacobian-vector
75: product, y = F'(u)*a:
76: y = (F(u + ha) - F(u)) /h,
77: where F = nonlinear function, as set by SNESSetFunction()
78: u = current iterate
79: h = difference interval
80: */
81: PetscErrorCode SNESMatrixFreeMult2_Private(Mat mat,Vec a,Vec y)
82: {
83: MFCtx_Private *ctx;
84: SNES snes;
85: PetscReal h,norm,sum,umin,noise;
86: PetscScalar hs,dot,mone = -1.0;
87: Vec w,U,F;
88: PetscErrorCode ierr,(*eval_fct)(SNES,Vec,Vec);
89: MPI_Comm comm;
90: PetscInt iter;
94: /* We log matrix-free matrix-vector products separately, so that we can
95: separate the performance monitoring from the cases that use conventional
96: storage. We may eventually modify event logging to associate events
97: with particular objects, hence alleviating the more general problem. */
98: PetscLogEventBegin(MAT_MultMatrixFree,a,y,0,0);
100: PetscObjectGetComm((PetscObject)mat,&comm);
101: MatShellGetContext(mat,(void **)&ctx);
102: snes = ctx->snes;
103: w = ctx->w;
104: umin = ctx->umin;
106: SNESGetSolution(snes,&U);
107: eval_fct = SNESComputeFunction;
108: SNESGetFunction(snes,&F,PETSC_NULL,PETSC_NULL);
110: /* Determine a "good" step size, h */
111: if (ctx->need_h) {
113: /* Use Jorge's method to compute h */
114: if (ctx->jorge) {
115: DiffParameterCompute_More(snes,ctx->data,U,a,&noise,&h);
117: /* Use the Brown/Saad method to compute h */
118: } else {
119: /* Compute error if desired */
120: SNESGetIterationNumber(snes,&iter);
121: if ((ctx->need_err) || ((ctx->compute_err_freq) && (ctx->compute_err_iter != iter) && (!((iter-1)%ctx->compute_err_freq)))) {
122: /* Use Jorge's method to compute noise */
123: DiffParameterCompute_More(snes,ctx->data,U,a,&noise,&h);
124: ctx->error_rel = sqrt(noise);
125: PetscLogInfo(snes,"SNESMatrixFreeMult2_Private: Using Jorge's noise: noise=%g, sqrt(noise)=%g, h_more=%g\n",noise,ctx->error_rel,h);
126: ctx->compute_err_iter = iter;
127: ctx->need_err = PETSC_FALSE;
128: }
130: VecDotBegin(U,a,&dot);
131: VecNormBegin(a,NORM_1,&sum);
132: VecNormBegin(a,NORM_2,&norm);
133: VecDotEnd(U,a,&dot);
134: VecNormEnd(a,NORM_1,&sum);
135: VecNormEnd(a,NORM_2,&norm);
138: /* Safeguard for step sizes too small */
139: if (sum == 0.0) {dot = 1.0; norm = 1.0;}
140: #if defined(PETSC_USE_COMPLEX)
141: else if (PetscAbsScalar(dot) < umin*sum && PetscRealPart(dot) >= 0.0) dot = umin*sum;
142: else if (PetscAbsScalar(dot) < 0.0 && PetscRealPart(dot) > -umin*sum) dot = -umin*sum;
143: #else
144: else if (dot < umin*sum && dot >= 0.0) dot = umin*sum;
145: else if (dot < 0.0 && dot > -umin*sum) dot = -umin*sum;
146: #endif
147: h = PetscRealPart(ctx->error_rel*dot/(norm*norm));
148: }
149: } else {
150: h = ctx->h;
151: }
152: if (!ctx->jorge || !ctx->need_h) PetscLogInfo(snes,"SNESMatrixFreeMult2_Private: h = %g\n",h);
154: /* Evaluate function at F(u + ha) */
155: hs = h;
156: VecWAXPY(&hs,a,U,w);
157: eval_fct(snes,w,y);
158: VecAXPY(&mone,F,y);
159: hs = 1.0/hs;
160: VecScale(&hs,y);
161: if (ctx->sp) {MatNullSpaceRemove(ctx->sp,y,PETSC_NULL);}
163: PetscLogEventEnd(MAT_MultMatrixFree,a,y,0,0);
164: return(0);
165: }
169: /*@C
170: SNESMatrixFreeMatCreate2 - Creates a matrix-free matrix
171: context for use with a SNES solver. This matrix can be used as
172: the Jacobian argument for the routine SNESSetJacobian().
174: Input Parameters:
175: . snes - the SNES context
176: . x - vector where SNES solution is to be stored.
178: Output Parameter:
179: . J - the matrix-free matrix
181: Level: advanced
183: Notes:
184: The matrix-free matrix context merely contains the function pointers
185: and work space for performing finite difference approximations of
186: Jacobian-vector products, J(u)*a, via
188: $ J(u)*a = [J(u+h*a) - J(u)]/h,
189: $ where by default
190: $ h = error_rel*u'a/||a||^2 if |u'a| > umin*||a||_{1}
191: $ = error_rel*umin*sign(u'a)*||a||_{1}/||a||^2 otherwise
192: $ where
193: $ error_rel = square root of relative error in
194: $ function evaluation
195: $ umin = minimum iterate parameter
196: $ Alternatively, the differencing parameter, h, can be set using
197: $ Jorge's nifty new strategy if one specifies the option
198: $ -snes_mf_jorge
200: The user can set these parameters via MatSNESMFSetFunctionError().
201: See the nonlinear solvers chapter of the users manual for details.
203: The user should call MatDestroy() when finished with the matrix-free
204: matrix context.
206: Options Database Keys:
207: $ -snes_mf_err <error_rel>
208: $ -snes_mf_unim <umin>
209: $ -snes_mf_compute_err
210: $ -snes_mf_freq_err <freq>
211: $ -snes_mf_jorge
213: .keywords: SNES, default, matrix-free, create, matrix
215: .seealso: MatDestroy(), MatSNESMFSetFunctionError()
216: @*/
217: PetscErrorCode SNESDefaultMatrixFreeCreate2(SNES snes,Vec x,Mat *J)
218: {
219: MPI_Comm comm;
220: MFCtx_Private *mfctx;
222: PetscInt n,nloc;
223: PetscTruth flg;
224: char p[64];
227: PetscNew(MFCtx_Private,&mfctx);
228: PetscMemzero(mfctx,sizeof(MFCtx_Private));
229: PetscLogObjectMemory(snes,sizeof(MFCtx_Private));
230: mfctx->sp = 0;
231: mfctx->snes = snes;
232: mfctx->error_rel = PETSC_SQRT_MACHINE_EPSILON;
233: mfctx->umin = 1.e-6;
234: mfctx->h = 0.0;
235: mfctx->need_h = PETSC_TRUE;
236: mfctx->need_err = PETSC_FALSE;
237: mfctx->compute_err = PETSC_FALSE;
238: mfctx->compute_err_freq = 0;
239: mfctx->compute_err_iter = -1;
240: PetscOptionsGetReal(snes->prefix,"-snes_mf_err",&mfctx->error_rel,PETSC_NULL);
241: PetscOptionsGetReal(snes->prefix,"-snes_mf_umin",&mfctx->umin,PETSC_NULL);
242: PetscOptionsHasName(snes->prefix,"-snes_mf_jorge",&mfctx->jorge);
243: PetscOptionsHasName(snes->prefix,"-snes_mf_compute_err",&mfctx->compute_err);
244: PetscOptionsGetInt(snes->prefix,"-snes_mf_freq_err",&mfctx->compute_err_freq,&flg);
245: if (flg) {
246: if (mfctx->compute_err_freq < 0) mfctx->compute_err_freq = 0;
247: mfctx->compute_err = PETSC_TRUE;
248: }
249: if (mfctx->compute_err) mfctx->need_err = PETSC_TRUE;
250: if (mfctx->jorge || mfctx->compute_err) {
251: DiffParameterCreate_More(snes,x,&mfctx->data);
252: } else mfctx->data = 0;
254: PetscOptionsHasName(PETSC_NULL,"-help",&flg);
255: PetscStrcpy(p,"-");
256: if (snes->prefix) PetscStrcat(p,snes->prefix);
257: if (flg) {
258: PetscPrintf(snes->comm," Matrix-free Options (via SNES):\n");
259: PetscPrintf(snes->comm," %ssnes_mf_err <err>: set sqrt of relative error in function (default %g)\n",p,mfctx->error_rel);
260: PetscPrintf(snes->comm," %ssnes_mf_umin <umin>: see users manual (default %g)\n",p,mfctx->umin);
261: PetscPrintf(snes->comm," %ssnes_mf_jorge: use Jorge More's method\n",p);
262: PetscPrintf(snes->comm," %ssnes_mf_compute_err: compute sqrt or relative error in function\n",p);
263: PetscPrintf(snes->comm," %ssnes_mf_freq_err <freq>: frequency to recompute this (default only once)\n",p);
264: PetscPrintf(snes->comm," %ssnes_mf_noise_file <file>: set file for printing noise info\n",p);
265: }
266: VecDuplicate(x,&mfctx->w);
267: PetscObjectGetComm((PetscObject)x,&comm);
268: VecGetSize(x,&n);
269: VecGetLocalSize(x,&nloc);
270: MatCreate(comm,nloc,n,n,n,J);
271: MatSetType(*J,MATSHELL);
272: MatShellSetContext(*J,mfctx);
273: MatShellSetOperation(*J,MATOP_MULT,(void(*)(void))SNESMatrixFreeMult2_Private);
274: MatShellSetOperation(*J,MATOP_DESTROY,(void(*)(void))SNESMatrixFreeDestroy2_Private);
275: MatShellSetOperation(*J,MATOP_VIEW,(void(*)(void))SNESMatrixFreeView2_Private);
277: PetscLogObjectParent(*J,mfctx->w);
278: PetscLogObjectParent(snes,*J);
279: return(0);
280: }
284: /*@C
285: SNESDefaultMatrixFreeSetParameters2 - Sets the parameters for the approximation of
286: matrix-vector products using finite differences.
288: $ J(u)*a = [J(u+h*a) - J(u)]/h where
290: either the user sets h directly here, or this parameter is computed via
292: $ h = error_rel*u'a/||a||^2 if |u'a| > umin*||a||_{1}
293: $ = error_rel*umin*sign(u'a)*||a||_{1}/||a||^2 else
294: $
296: Input Parameters:
297: + mat - the matrix
298: . error_rel - relative error (should be set to the square root of
299: the relative error in the function evaluations)
300: . umin - minimum allowable u-value
301: - h - differencing parameter
303: Level: advanced
305: Notes:
306: If the user sets the parameter h directly, then this value will be used
307: instead of the default computation indicated above.
309: .keywords: SNES, matrix-free, parameters
311: .seealso: MatCreateSNESMF()
312: @*/
313: PetscErrorCode SNESDefaultMatrixFreeSetParameters2(Mat mat,PetscReal error,PetscReal umin,PetscReal h)
314: {
315: MFCtx_Private *ctx;
319: MatShellGetContext(mat,(void **)&ctx);
320: if (ctx) {
321: if (error != PETSC_DEFAULT) ctx->error_rel = error;
322: if (umin != PETSC_DEFAULT) ctx->umin = umin;
323: if (h != PETSC_DEFAULT) {
324: ctx->h = h;
325: ctx->need_h = PETSC_FALSE;
326: }
327: }
328: return(0);
329: }
331: PetscErrorCode SNESUnSetMatrixFreeParameter(SNES snes)
332: {
333: MFCtx_Private *ctx;
335: Mat mat;
338: SNESGetJacobian(snes,&mat,PETSC_NULL,PETSC_NULL,PETSC_NULL);
339: MatShellGetContext(mat,(void **)&ctx);
340: if (ctx) ctx->need_h = PETSC_TRUE;
341: return(0);
342: }
343: