// run_iterations solves the nonlinear problem func run_iterations(t, Δt float64, d *Domain, dc *DynCoefs, sum *Summary, dbgKb DebugKb_t) (diverging bool, err error) { // zero accumulated increments la.VecFill(d.Sol.ΔY, 0) // calculate global starred vectors and interpolate starred variables from nodes to integration points if !d.Sim.Data.Steady { // compute starred vectors for _, I := range d.T1eqs { d.Sol.Psi[I] = dc.β1*d.Sol.Y[I] + dc.β2*d.Sol.Dydt[I] } for _, I := range d.T2eqs { d.Sol.Zet[I] = dc.α1*d.Sol.Y[I] + dc.α2*d.Sol.Dydt[I] + dc.α3*d.Sol.D2ydt2[I] d.Sol.Chi[I] = dc.α4*d.Sol.Y[I] + dc.α5*d.Sol.Dydt[I] + dc.α6*d.Sol.D2ydt2[I] } // set internal starred variables for _, e := range d.Elems { err = e.InterpStarVars(d.Sol) if err != nil { err = chk.Err("cannot compute starred variables:\n%v", err) return } } } // auxiliary variables var it int var largFb, largFb0, Lδu float64 var prevFb, prevLδu float64 dat := d.Sim.Solver // message if dat.ShowR { io.Pf("\n%13s%4s%23s%23s\n", "t", "it", "largFb", "Lδu") defer func() { io.Pf("%13.6e%4d%23.15e%23.15e\n", t, it, largFb, Lδu) }() } // iterations for it = 0; it < dat.NmaxIt; it++ { // assemble right-hand side vector (fb) with negative of residuals la.VecFill(d.Fb, 0) for _, e := range d.Elems { err = e.AddToRhs(d.Fb, d.Sol) if err != nil { return } } // join all fb if d.Distr { mpi.AllReduceSum(d.Fb, d.Wb) // this must be done here because there might be nodes sharing boundary conditions } // point natural boundary conditions; e.g. concentrated loads d.PtNatBcs.AddToRhs(d.Fb, t) // essential boundary conditioins; e.g. constraints d.EssenBcs.AddToRhs(d.Fb, d.Sol) // find largest absolute component of fb largFb = la.VecLargest(d.Fb, 1) // save residual if d.Sim.Data.Stat { if sum != nil { sum.Resids.Append(it == 0, largFb) } } // check largFb value if it == 0 { // store largest absolute component of fb largFb0 = largFb } else { // check convergence on Lf0 if largFb < dat.FbTol*largFb0 { // converged on fb break } // check convergence on fb_min if largFb < dat.FbMin { // converged with smallest value of fb break } } // check divergence on fb if it > 1 && dat.DvgCtrl { if largFb > prevFb { diverging = true break } } prevFb = largFb // assemble Jacobian matrix do_asm_fact := (it == 0 || !dat.CteTg) if do_asm_fact { // assemble element matrices d.Kb.Start() for _, e := range d.Elems { err = e.AddToKb(d.Kb, d.Sol, it == 0) if err != nil { return } } // debug if dbgKb != nil { dbgKb(d, it) } // join A and tr(A) matrices into Kb if d.Proc == 0 { d.Kb.PutMatAndMatT(&d.EssenBcs.A) } // initialise linear solver if d.InitLSol { err = d.LinSol.InitR(d.Kb, d.Sim.LinSol.Symmetric, d.Sim.LinSol.Verbose, d.Sim.LinSol.Timing) if err != nil { err = chk.Err("cannot initialise linear solver:\n%v", err) return } d.InitLSol = false } // perform factorisation err = d.LinSol.Fact() if err != nil { err = chk.Err("factorisation failed:\n%v", err) return } } // solve for wb := δyb err = d.LinSol.SolveR(d.Wb, d.Fb, false) if err != nil { err = chk.Err("solve failed:%v\n", err) return } // update primary variables (y) for i := 0; i < d.Ny; i++ { d.Sol.Y[i] += d.Wb[i] // y += δy d.Sol.ΔY[i] += d.Wb[i] // ΔY += δy } if !d.Sim.Data.Steady { for _, I := range d.T1eqs { d.Sol.Dydt[I] = dc.β1*d.Sol.Y[I] - d.Sol.Psi[I] } for _, I := range d.T2eqs { d.Sol.Dydt[I] = dc.α4*d.Sol.Y[I] - d.Sol.Chi[I] d.Sol.D2ydt2[I] = dc.α1*d.Sol.Y[I] - d.Sol.Zet[I] } } // update Lagrange multipliers (λ) for i := 0; i < d.Nlam; i++ { d.Sol.L[i] += d.Wb[d.Ny+i] // λ += δλ } // backup / restore if it == 0 { // create backup copy of all secondary variables for _, e := range d.ElemIntvars { e.BackupIvs(false) } } else { // recover last converged state from backup copy for _, e := range d.ElemIntvars { e.RestoreIvs(false) } } // update secondary variables for _, e := range d.Elems { err = e.Update(d.Sol) if err != nil { break } } // compute RMS norm of δu and check convegence on δu Lδu = la.VecRmsErr(d.Wb[:d.Ny], dat.Atol, dat.Rtol, d.Sol.Y[:d.Ny]) // message if dat.ShowR { io.Pf("%13.6e%4d%23.15e%23.15e\n", t, it, largFb, Lδu) } // stop if converged on δu if Lδu < dat.Itol { break } // check divergence on Lδu if it > 1 && dat.DvgCtrl { if Lδu > prevLδu { diverging = true break } } prevLδu = Lδu } // check if iterations diverged if it == dat.NmaxIt { err = chk.Err("max number of iterations reached: it = %d\n", it) } return }
// run_iterations solves the nonlinear problem func run_iterations(t, Δt float64, d *Domain, sum *Summary) (diverging, ok bool) { // zero accumulated increments la.VecFill(d.Sol.ΔY, 0) // calculate global starred vectors and interpolate starred variables from nodes to integration points if LogErr(d.star_vars(Δt), "cannot compute starred variables") { return } // auxiliary variables var it int var largFb, largFb0, Lδu float64 var prevFb, prevLδu float64 // message if Global.Sim.Data.ShowR { io.Pf("\n%13s%4s%23s%23s\n", "t", "it", "largFb", "Lδu") defer func() { io.Pf("%13.6e%4d%23.15e%23.15e\n", t, it, largFb, Lδu) }() } // iterations for it = 0; it < Global.Sim.Solver.NmaxIt; it++ { // assemble right-hand side vector (fb) with negative of residuals la.VecFill(d.Fb, 0) for _, e := range d.Elems { if !e.AddToRhs(d.Fb, d.Sol) { break } } if Stop() { return } // join all fb if Global.Distr { mpi.AllReduceSum(d.Fb, d.Wb) // this must be done here because there might be nodes sharing boundary conditions } // point natural boundary conditions; e.g. concentrated loads d.PtNatBcs.AddToRhs(d.Fb, t) // essential boundary conditioins; e.g. constraints d.EssenBcs.AddToRhs(d.Fb, d.Sol) // debug if Global.Debug { //la.PrintVec("fb", d.Fb[:d.Ny], "%13.10f ", false) //panic("stop") } // find largest absolute component of fb largFb = la.VecLargest(d.Fb, 1) // save residual if Global.Stat { sum.Resids.Append(it == 0, largFb) } // check largFb value if it == 0 { // store largest absolute component of fb largFb0 = largFb } else { // check convergence on Lf0 if largFb < Global.Sim.Solver.FbTol*largFb0 { // converged on fb break } // check convergence on fb_min if largFb < Global.Sim.Solver.FbMin { // converged with smallest value of fb break } } // check divergence on fb if it > 1 && Global.Sim.Solver.DvgCtrl { if largFb > prevFb { diverging = true break } } prevFb = largFb // assemble Jacobian matrix do_asm_fact := (it == 0 || !Global.Sim.Data.CteTg) if do_asm_fact { // assemble element matrices d.Kb.Start() for _, e := range d.Elems { if !e.AddToKb(d.Kb, d.Sol, it == 0) { break } } if Stop() { return } // debug if Global.DebugKb != nil { Global.DebugKb(d, it) } // join A and tr(A) matrices into Kb if Global.Root { d.Kb.PutMatAndMatT(&d.EssenBcs.A) } // initialise linear solver if d.InitLSol { if LogErr(d.LinSol.InitR(d.Kb, Global.Sim.LinSol.Symmetric, Global.Sim.LinSol.Verbose, Global.Sim.LinSol.Timing), "cannot initialise linear solver") { return } d.InitLSol = false } // perform factorisation LogErr(d.LinSol.Fact(), "factorisation") if Stop() { return } } // debug //KK := d.Kb.ToMatrix(nil).ToDense() //la.PrintMat("KK", KK, "%20.10f", false) //panic("stop") // solve for wb := δyb LogErr(d.LinSol.SolveR(d.Wb, d.Fb, false), "solve") if Stop() { return } // debug if Global.Debug { //la.PrintVec("wb", d.Wb[:d.Ny], "%13.10f ", false) } // update primary variables (y) for i := 0; i < d.Ny; i++ { d.Sol.Y[i] += d.Wb[i] // y += δy d.Sol.ΔY[i] += d.Wb[i] // ΔY += δy } if !Global.Sim.Data.Steady { for _, I := range d.T1eqs { d.Sol.Dydt[I] = Global.DynCoefs.β1*d.Sol.Y[I] - d.Sol.Psi[I] } for _, I := range d.T2eqs { d.Sol.Dydt[I] = Global.DynCoefs.α4*d.Sol.Y[I] - d.Sol.Chi[I] d.Sol.D2ydt2[I] = Global.DynCoefs.α1*d.Sol.Y[I] - d.Sol.Zet[I] } } // update Lagrange multipliers (λ) for i := 0; i < d.Nlam; i++ { d.Sol.L[i] += d.Wb[d.Ny+i] // λ += δλ } // backup / restore if it == 0 { // create backup copy of all secondary variables for _, e := range d.ElemIntvars { e.BackupIvs(false) } } else { // recover last converged state from backup copy for _, e := range d.ElemIntvars { e.RestoreIvs(false) } } // update secondary variables for _, e := range d.Elems { if !e.Update(d.Sol) { break } } if Stop() { return } // compute RMS norm of δu and check convegence on δu Lδu = la.VecRmsErr(d.Wb[:d.Ny], Global.Sim.Solver.Atol, Global.Sim.Solver.Rtol, d.Sol.Y[:d.Ny]) // message if Global.Sim.Data.ShowR { io.Pf("%13.6e%4d%23.15e%23.15e\n", t, it, largFb, Lδu) } // stop if converged on δu if Lδu < Global.Sim.Solver.Itol { break } // check divergence on Lδu if it > 1 && Global.Sim.Solver.DvgCtrl { if Lδu > prevLδu { diverging = true break } } prevLδu = Lδu } // check if iterations diverged if it == Global.Sim.Solver.NmaxIt { io.PfMag("max number of iterations reached: it = %d\n", it) return } // success ok = true return }
// 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 }