[petsc-users] How to choose mat_mffd_err in JFNK
Smith, Barry F.
bsmith at mcs.anl.gov
Thu Aug 15 02:30:52 CDT 2019
> On Aug 15, 2019, at 12:36 AM, Tang, Qi <tangqi at msu.edu> wrote:
>
> Thanks, it works. snes_mf_jorge works for me.
Great.
> It appears to compute h in every ksp.
Each matrix vector product or each KSPSolve()? From the code looks like each matrix-vector product.
>
> Without -snes_mf_jorge, it is not working. For some reason, it only computes h once, but that h is bad. My gmres residual is not decaying.
>
> Indeed, the noise in my function becomes larger when I refine the mesh. I think it makes sense as I use the same time step for different meshes (that is the goal of the preconditioning). However, even when the algorithm is working, sqrt(noise) is much less than the good mat_mffd_err I previously found (10^-6 vs 10^-3). I do not understand why.
I have no explanation. The details of the code that computes err are difficult to trace exactly; would take a while. Perhaps there is some parameter in there that is too "conservative"?
>
> Although snes_mf_jorge is working, it is very expensive, as it has to evaluate F many times when estimating h. Unfortunately, to achieve the nonlinearity, I have to assemble some operators inside my F. There seem no easy solutions.
>
> I will try to compute h multiple times without using snes_mf_jorge. But let me know if you have other suggestions. Thanks!
Yes, it seems to me computing the err less often would be a way to make the code go faster. I looked at the code more closely and noticed a couple of things.
if (ctx->jorge) {
ierr = SNESDiffParameterCompute_More(snes,ctx->data,U,a,&noise,&h);CHKERRQ(ierr);
/* Use the Brown/Saad method to compute h */
} else {
/* Compute error if desired */
ierr = SNESGetIterationNumber(snes,&iter);CHKERRQ(ierr);
if ((ctx->need_err) || ((ctx->compute_err_freq) && (ctx->compute_err_iter != iter) && (!((iter-1)%ctx->compute_err_freq)))) {
/* Use Jorge's method to compute noise */
ierr = SNESDiffParameterCompute_More(snes,ctx->data,U,a,&noise,&h);CHKERRQ(ierr);
ctx->error_rel = PetscSqrtReal(noise);
ierr = PetscInfo3(snes,"Using Jorge's noise: noise=%g, sqrt(noise)=%g, h_more=%g\n",(double)noise,(double)ctx->error_rel,(double)h);CHKERRQ(ierr);
ctx->compute_err_iter = iter;
ctx->need_err = PETSC_FALSE;
}
So if jorge is set it uses the jorge algorithm for each matrix multiple to compute a new err and h. If jorge is not set but -snes_mf_compute_err is set then it computes a new err and h depending on the parameters (you can run with -info and grep for noise to see the PetscInfo lines printed (maybe add iter to the output to see how often it is recomputed)
if ((ctx->need_err) || ((ctx->compute_err_freq) && (ctx->compute_err_iter != iter) && (!((iter-1)%ctx->compute_err_freq)))) {
so the compute_err_freq determines when the err and h are recomputed. The logic is a bit strange so I cannot decipher exactly how often it is recomputing. I'm guess at the first Newton iteration and then some number of iterations later.
You could try to rig it so that it is at every new Newton step (this would mean when computing f(x + h a) - f(x) for each new x it will recompute I think).
A more "research type" approach to try to reduce the work to a reasonable level would be to keep the same err until "things start to go bad" and then recompute it. But how to measure "when things start to go bad?" It is related to GMRES stagnating, so you could for example track the convergence of GMRES and if it is less than expected, kill the linear solve, reset the computation of err and then start the KSP at the same point as before. But this could be terribly expensive since all the steps in the most recent KSP are lost.
Another possibility is to assume that the err is valid in a certain size ball around the current x. When x + ha or the new x is outside that ball then recompute the err. But how to choose the ball size and is there a way to adjust the ball size depending on how the computations proceed. For example if everything is going great you could slowly increase the size of the ball and hope for the best; but how to detect if the ball got too big (as above but terribly expensive)?
Track the size of the err each time it is computed. If it stays about the same for a small number of times just freeze it for a while at that value? But again how to determine when it is no longer good?
Just a few wild thoughts, I really have no solid ideas on how to reduce the work requirements.
Barry
>
> Qi
>
>
> From: Smith, Barry F. <bsmith at mcs.anl.gov>
> Sent: Wednesday, August 14, 2019 10:57 PM
> To: Tang, Qi <tangqi at msu.edu>
> Cc: petsc-users at mcs.anl.gov <petsc-users at mcs.anl.gov>
> Subject: Re: [petsc-users] How to choose mat_mffd_err in JFNK
>
>
>
> > On Aug 14, 2019, at 9:41 PM, Tang, Qi <tangqi at msu.edu> wrote:
> >
> > Thanks for the help, Barry. I tired both ds and wp, and again it depends on if I could find the correct parameter set. It is getting harder as I refine the mesh.
> >
> > So I try to use SNESDefaultMatrixFreeCreate2, SNESMatrixFreeMult2_Private or SNESDiffParameterCompute_More in mfem. But it looks like these functions are not in linked in petscsnes.h. How could I call them?
>
> They may not be listed in petscsnes.h but I think they should be in the library. You can just stick the prototypes for the functions anywhere you need them for now.
>
>
> You should be able to use
>
> ierr = PetscOptionsInt("-snes_mf_version","Matrix-Free routines version 1 or 2","None",snes->mf_version,&snes->mf_version,0);CHKERRQ(ierr);
>
> then this info is passed with
>
> if (snes->mf) {
> ierr = SNESSetUpMatrixFree_Private(snes, snes->mf_operator, snes->mf_version);CHKERRQ(ierr);
> }
>
> this routine has
>
> if (version == 1) {
> ierr = MatCreateSNESMF(snes,&J);CHKERRQ(ierr);
> ierr = MatMFFDSetOptionsPrefix(J,((PetscObject)snes)->prefix);CHKERRQ(ierr);
> ierr = MatSetFromOptions(J);CHKERRQ(ierr);
> } else if (version == 2) {
> if (!snes->vec_func) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"SNESSetFunction() must be called first");
> #if !defined(PETSC_USE_COMPLEX) && !defined(PETSC_USE_REAL_SINGLE) && !defined(PETSC_USE_REAL___FLOAT128) && !defined(PETSC_USE_REAL___FP16)
> ierr = SNESDefaultMatrixFreeCreate2(snes,snes->vec_func,&J);CHKERRQ(ierr);
> #else
>
> and this routine has
>
> ierr = VecDuplicate(x,&mfctx->w);CHKERRQ(ierr);
> ierr = PetscObjectGetComm((PetscObject)x,&comm);CHKERRQ(ierr);
> ierr = VecGetSize(x,&n);CHKERRQ(ierr);
> ierr = VecGetLocalSize(x,&nloc);CHKERRQ(ierr);
> ierr = MatCreate(comm,J);CHKERRQ(ierr);
> ierr = MatSetSizes(*J,nloc,n,n,n);CHKERRQ(ierr);
> ierr = MatSetType(*J,MATSHELL);CHKERRQ(ierr);
> ierr = MatShellSetContext(*J,mfctx);CHKERRQ(ierr);
> ierr = MatShellSetOperation(*J,MATOP_MULT,(void (*)(void))SNESMatrixFreeMult2_Private);CHKERRQ(ierr);
> ierr = MatShellSetOperation(*J,MATOP_DESTROY,(void (*)(void))SNESMatrixFreeDestroy2_Private);CHKERRQ(ierr);
> ierr = MatShellSetOperation(*J,MATOP_VIEW,(void (*)(void))SNESMatrixFreeView2_Private);CHKERRQ(ierr);
> ierr = MatSetUp(*J);CHKERRQ(ierr);
>
>
>
>
>
>
> >
> > Also, could I call SNESDiffParameterCompute_More in snes_monitor? But it needs "a" in (F(u + ha) - F(u)) /h as the input. So I am not sure how I could properly use it. Maybe use SNESDefaultMatrixFreeCreate2 inside SNESSetJacobian would be easier to try?
>
> If you use the flag -snes_mf_noise_file filename when it runs it will save all the noise information it computes along the way to that file (yes it is crude and doesn't match the PETSc Viewer/monitor style but it should work).
>
> Thus I think you can use it and get all the possible monitoring information without actually writing any code. Just
>
> -snes_mf_version 2
> -snes_mf_noise_file filename
>
>
>
> Barry
>
> >
> > Thanks again,
> > Qi
> >
> > From: Smith, Barry F. <bsmith at mcs.anl.gov>
> > Sent: Tuesday, August 13, 2019 9:07 PM
> > To: Tang, Qi <tangqi at msu.edu>
> > Cc: petsc-users at mcs.anl.gov <petsc-users at mcs.anl.gov>
> > Subject: Re: [petsc-users] How to choose mat_mffd_err in JFNK
> >
> >
> >
> > > On Aug 13, 2019, at 7:27 PM, Tang, Qi via petsc-users <petsc-users at mcs.anl.gov> wrote:
> > >
> > > Hi,
> > > I am using JFNK, inexact Newton and a shell "physics-based" preconditioning to solve some multiphysics problems. I have been playing with mat_mffd_err, and it gives me some results I do not fully understand.
> > >
> > > I believe the default value of mat_mffd_err is 10^-8 for double precision, which seem too large for my problems. When I test a problem essentially still in the linear regime, I expect my converged "unpreconditioned resid norm of KSP" should be identical to "SNES Function norm" (Should I?). This is exactly what I found if I could find a good mat_mffd_err, normally between 10^-3 to 10^-5. So when it happens, the whole algorithm works as expected. When those two norms are off, the inexact Newton becomes very inefficient. For instance, it may take many ksp iterations to converge but the snes norm is only reduced slightly.
> > >
> > > According to the manual, mat_mffd_err should be "square root of relative error in function evaluation". But is there a simple way to estimate it?
> >
> > First a related note: there are two different algorithms that PETSc provides for computing the factor h using the err parameter. They can be set with -mat_mffd_type - wp or ds (see MATMFFD_WP or MATMFFD_DS) some people have better luck with one or the other for their problem.
> >
> >
> > There is some code in PETSc to compute what is called the "noise" of the function which in theory leads to a better err value. For example if say the last four digits of your function are "noise" (that is meaningless stuff) which is possible for complicated multiphysics problems due to round off in the function evaluations then you should use an err that is 2 digits bigger than the default (note this is just the square root of the "function epsilon" instead of the machine epsilon, because the "function epsilon" is much larger than the machine epsilon).
> >
> > We never had a great model for how to hook the noise computation code up into SNES so it is a bit disconnected, we should revisit this. Perhaps you will find it useful and we can figure out together how to hook it in cleanly for use.
> >
> > The code that computes and reports the noise is in the directory src/snes/interface/noise
> >
> > You can use this version with the option -snes_mf_version 2 (version 1 is the normal behavior)
> >
> > The code has the ability to print to a file or the screen information about the noise it is finding, maybe with command line options, you'll have to look directly at the code to see how.
> >
> > I don't like the current setup because if you use the noise based computation of h you have to use SNESMatrixFreeMult2_Private() which is a slightly different routine for doing the actually differencing than the two MATMFFD_WP or MATMFFD_DS that are normally used. I don't know why it can't use the standard differencing routines but call the noise routine to compute h (just requires some minor code refactoring). Also I don't see an automatic way to just compute the noise of a function at a bunch of points independent of actually using the noise to compute and use h. It is sort of there in the routine SNESDiffParameterCompute_More() but that routine is not documented or user friendly.
> >
> > I would start by rigging up calls to SNESDiffParameterCompute_More() to see what it says the noise of your function is, based on your hand determined values optimal values for err it should be roughly the square of them. Then if this makes sense you can trying using the -snes_mf_version 2 code to see if it helps the matrix free multiple behave consistently well for your problem by adaptively computing the err and using that in computing h.
> >
> > Good luck and I'd love to hear back from you if it helps and suggestions/code for making this more integrated with SNES so it is easier to use. For example perhaps we want a function SNESComputeNoise() that uses SNESDiffParameterCompute_More() to compute and report the noise for a range of values of u.
> >
> > Barry
> >
> >
> >
> >
> >
> > >
> > > Is there anything else I could possibly tune in this context?
> > >
> > > The discretization is through mfem and I use standard H1 for my problem.
> > >
> > > Thanks,
> > > Qi
More information about the petsc-users
mailing list