Beispiel #1
0
// 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
}
Beispiel #2
0
// 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
}