// Solve solves non-linear problem f(x) == 0 func (o *NlSolver) Solve(x []float64, silent bool) (err error) { // compute scaling vector la.VecScaleAbs(o.scal, o.atol, o.rtol, x) // scal := Atol + Rtol*abs(x) // evaluate function @ x err = o.Ffcn(o.fx, x) // fx := f(x) o.NFeval, o.NJeval = 1, 0 if err != nil { return } // show message if !silent { o.msg("", 0, 0, 0, true, false) } // iterations var Ldx, Ldx_prev, Θ float64 // RMS norm of delta x, convergence rate var fx_max float64 var nfv int for o.It = 0; o.It < o.MaxIt; o.It++ { // check convergence on f(x) fx_max = la.VecLargest(o.fx, 1.0) // den = 1.0 if fx_max < o.ftol { if !silent { o.msg("fx_max(ini)", o.It, Ldx, fx_max, false, true) } break } // show message if !silent { o.msg("", o.It, Ldx, fx_max, false, false) } // output if o.Out != nil { o.Out(x) } // evaluate Jacobian @ x if o.It == 0 || !o.CteJac { if o.useDn { err = o.JfcnDn(o.J, x) } else { if o.numJ { err = Jacobian(&o.Jtri, o.Ffcn, x, o.fx, o.w, false) o.NFeval += o.neq } else { err = o.JfcnSp(&o.Jtri, x) } } o.NJeval += 1 if err != nil { return } } // dense solution if o.useDn { // invert matrix err = la.MatInvG(o.Ji, o.J, 1e-10) if err != nil { return chk.Err(_nls_err1, err.Error()) } // solve linear system (compute mdx) and compute lin-search data o.φ = 0.0 for i := 0; i < o.neq; i++ { o.mdx[i], o.dφdx[i] = 0.0, 0.0 for j := 0; j < o.neq; j++ { o.mdx[i] += o.Ji[i][j] * o.fx[j] // mdx = inv(J) * fx o.dφdx[i] += o.J[j][i] * o.fx[j] // dφdx = tra(J) * fx } o.φ += o.fx[i] * o.fx[i] } o.φ *= 0.5 // sparse solution } else { // init sparse solver if o.It == 0 { symmetric, verbose, timing := false, false, false err := o.lis.InitR(&o.Jtri, symmetric, verbose, timing) if err != nil { return chk.Err(_nls_err9, err.Error()) } } // factorisation (must be done for all iterations) o.lis.Fact() // solve linear system => compute mdx o.lis.SolveR(o.mdx, o.fx, false) // mdx = inv(J) * fx false => !sumToRoot // compute lin-search data if o.Lsearch { o.φ = 0.5 * la.VecDot(o.fx, o.fx) la.SpTriMatTrVecMul(o.dφdx, &o.Jtri, o.fx) // dφdx := transpose(J) * fx } } //io.Pforan("φ = %v\n", o.φ) //io.Pforan("dφdx = %v\n", o.dφdx) // update x Ldx = 0.0 for i := 0; i < o.neq; i++ { o.x0[i] = x[i] x[i] -= o.mdx[i] Ldx += (o.mdx[i] / o.scal[i]) * (o.mdx[i] / o.scal[i]) } Ldx = math.Sqrt(Ldx / float64(o.neq)) // calculate fx := f(x) @ update x err = o.Ffcn(o.fx, x) o.NFeval += 1 if err != nil { return } // check convergence on f(x) => avoid line-search if converged already fx_max = la.VecLargest(o.fx, 1.0) // den = 1.0 if fx_max < o.ftol { if !silent { o.msg("fx_max", o.It, Ldx, fx_max, false, true) } break } // check convergence on Ldx if Ldx < o.fnewt { if !silent { o.msg("Ldx", o.It, Ldx, fx_max, false, true) } break } // call line-search => update x and fx if o.Lsearch { nfv, err = LineSearch(x, o.fx, o.Ffcn, o.mdx, o.x0, o.dφdx, o.φ, o.LsMaxIt, true) o.NFeval += nfv if err != nil { return chk.Err(_nls_err2, err.Error()) } Ldx = 0.0 for i := 0; i < o.neq; i++ { Ldx += ((x[i] - o.x0[i]) / o.scal[i]) * ((x[i] - o.x0[i]) / o.scal[i]) } Ldx = math.Sqrt(Ldx / float64(o.neq)) fx_max = la.VecLargest(o.fx, 1.0) // den = 1.0 if Ldx < o.fnewt { if !silent { o.msg("Ldx(linsrch)", o.It, Ldx, fx_max, false, true) } break } } // check convergence rate if o.It > 0 && o.ChkConv { Θ = Ldx / Ldx_prev if Θ > 0.99 { return chk.Err(_nls_err3, Θ, Ldx, Ldx_prev) } } Ldx_prev = Ldx } // output if o.Out != nil { o.Out(x) } // check convergence if o.It == o.MaxIt { err = chk.Err(_nls_err4, o.It) } return }
// Solve solves from (xa,ya) to (xb,yb) => find yb (stored in y) func (o *ODE) Solve(y []float64, x, xb, Δx float64, fixstp bool, args ...interface{}) (err error) { // check if xb < x { err = chk.Err(_ode_err3, xb, x) return } // derived variables o.fnewt = max(10.0*o.ϵ/o.Rtol, min(0.03, math.Sqrt(o.Rtol))) // initial step size Δx = min(Δx, xb-x) if fixstp { o.h = Δx } else { o.h = min(Δx, o.IniH) } o.hprev = o.h // output initial state if o.out != nil { o.out(true, o.h, x, y, args...) } // stat variables o.nfeval = 0 o.njeval = 0 o.nsteps = 0 o.naccepted = 0 o.nrejected = 0 o.ndecomp = 0 o.nlinsol = 0 o.nitmax = 0 // control variables o.doinit = true o.first = true o.last = false o.reject = false o.diverg = false o.dvfac = 0 o.η = 1.0 o.jacIsOK = false o.reuseJdec = false o.reuseJ = false o.nit = 0 o.hopt = o.h o.θ = o.θmax // local error indicator var rerr float64 // linear solver lsname := "umfpack" if o.Distr { lsname = "mumps" } o.lsolR = la.GetSolver(lsname) o.lsolC = la.GetSolver(lsname) // clean up and show stat before leaving defer func() { o.lsolR.Clean() o.lsolC.Clean() if !o.silent { o.Stat() } }() // first scaling variable la.VecScaleAbs(o.scal, o.Atol, o.Rtol, y) // o.scal := o.Atol + o.Rtol * abs(y) // fixed steps if fixstp { la.VecCopy(o.w[0], 1, y) // copy initial values to worksapce if o.Verbose { io.Pfgreen("x = %v\n", x) } for x < xb { //if x + o.h > xb { o.h = xb - x } if o.jac == nil { // numerical Jacobian if o.method == "Radau5" { o.nfeval += 1 o.fcn(o.f0, x, y, args...) } } o.reuseJdec = false o.reuseJ = false o.jacIsOK = false o.step(o, y, x, args...) o.nsteps += 1 o.doinit = false o.first = false o.hprev = o.h x += o.h o.accept(o, y) if o.out != nil { o.out(false, o.h, x, y, args...) } if o.Verbose { io.Pfgreen("x = %v\n", x) } } return } // first function evaluation o.nfeval += 1 o.fcn(o.f0, x, y, args...) // o.f0 := f(x,y) // time loop var dxmax, xstep, fac, div, dxnew, facgus, old_h, old_rerr float64 var dxratio float64 var failed bool for x < xb { dxmax, xstep = Δx, x+Δx failed = false for iss := 0; iss < o.NmaxSS+1; iss++ { // total number of substeps o.nsteps += 1 // error: did not converge if iss == o.NmaxSS { failed = true break } // converged? if x-xstep >= 0.0 { break } // step update rerr, err = o.step(o, y, x, args...) // initialise only once o.doinit = false // iterations diverging ? if o.diverg { o.diverg = false o.reject = true o.last = false o.h = o.dvfac * o.h continue } // step size change fac = min(o.Mfac, o.Mfac*float64(1+2*o.NmaxIt)/float64(o.nit+2*o.NmaxIt)) div = max(o.Mmin, min(o.Mmax, math.Pow(rerr, 0.25)/fac)) dxnew = o.h / div // accepted if rerr < 1.0 { // set flags o.naccepted += 1 o.first = false o.jacIsOK = false // update x and y o.hprev = o.h x += o.h o.accept(o, y) // output if o.out != nil { o.out(false, o.h, x, y, args...) } // converged ? if o.last { o.hopt = o.h // optimal h break } // predictive controller of Gustafsson if o.PredCtrl { if o.naccepted > 1 { facgus = (old_h / o.h) * math.Pow(math.Pow(rerr, 2.0)/old_rerr, 0.25) / o.Mfac facgus = max(o.Mmin, min(o.Mmax, facgus)) div = max(div, facgus) dxnew = o.h / div } old_h = o.h old_rerr = max(1.0e-2, rerr) } // calc new scal and f0 la.VecScaleAbs(o.scal, o.Atol, o.Rtol, y) // o.scal := o.Atol + o.Rtol * abs(y) o.nfeval += 1 o.fcn(o.f0, x, y, args...) // o.f0 := f(x,y) // new step size dxnew = min(dxnew, dxmax) if o.reject { // do not alow o.h to grow if previous was a reject dxnew = min(o.h, dxnew) } o.reject = false // do not reuse current Jacobian and decomposition by default o.reuseJdec = false // last step ? if x+dxnew-xstep >= 0.0 { o.last = true o.h = xstep - x } else { dxratio = dxnew / o.h o.reuseJdec = (o.θ <= o.θmax && dxratio >= o.C1h && dxratio <= o.C2h) if !o.reuseJdec { o.h = dxnew } } // check θ to decide if at least the Jacobian can be reused if !o.reuseJdec { o.reuseJ = (o.θ <= o.θmax) } // rejected } else { // set flags if o.naccepted > 0 { o.nrejected += 1 } o.reject = true o.last = false // new step size if o.first { o.h = 0.1 * o.h } else { o.h = dxnew } // last step if x+o.h > xstep { o.h = xstep - x } } } // sub-stepping failed if failed { err = chk.Err(_ode_err2, o.NmaxSS) break } } return }