// add_surfloads_to_rhs adds surfaces loads to rhs func (o *ElemU) add_surfloads_to_rhs(fb []float64, sol *Solution) (err error) { // debugging variables if o.Debug { la.VecFill(o.fex, 0) la.VecFill(o.fey, 0) if o.Ndim == 3 { la.VecFill(o.fez, 0) } } // compute surface integral var res float64 for _, nbc := range o.NatBcs { // function evaluation res = nbc.Fcn.F(sol.T, nil) // loop over ips of face for _, ipf := range o.IpsFace { // interpolation functions and gradients @ face iface := nbc.IdxFace err = o.Cell.Shp.CalcAtFaceIp(o.X, ipf, iface) if err != nil { return } Sf := o.Cell.Shp.Sf nvec := o.Cell.Shp.Fnvec // select natural boundary condition type switch nbc.Key { // distributed load case "qn", "qn0", "aqn": coef := ipf[3] * res * o.Thickness if sol.Axisym && nbc.Key == "aqn" { coef *= o.Cell.Shp.AxisymGetRadiusF(o.X, iface) } for j, m := range o.Cell.Shp.FaceLocalVerts[iface] { for i := 0; i < o.Ndim; i++ { r := o.Umap[i+m*o.Ndim] fb[r] += coef * Sf[j] * nvec[i] // +fe } if o.Debug { o.fex[m] += coef * Sf[j] * nvec[0] o.fey[m] += coef * Sf[j] * nvec[1] if o.Ndim == 3 { o.fez[m] += coef * Sf[j] * nvec[2] } } } } } } return }
// AddToRhs adds -R to global residual vector fb func (o *ElemP) AddToRhs(fb []float64, sol *Solution) (err error) { // clear variables if o.DoExtrap { la.VecFill(o.ρl_ex, 0) } // for each integration point β1 := sol.DynCfs.β1 nverts := o.Cell.Shp.Nverts var coef, plt, klr, ρL, ρl, Cpl float64 for idx, ip := range o.IpsElem { // interpolation functions, gradients and variables @ ip err = o.ipvars(idx, sol) if err != nil { return } coef = o.Cell.Shp.J * ip[3] S := o.Cell.Shp.S G := o.Cell.Shp.G // tpm variables plt = β1*o.pl - o.ψl[idx] klr = o.Mdl.Cnd.Klr(o.States[idx].A_sl) ρL = o.States[idx].A_ρL err = o.Mdl.CalcLs(o.res, o.States[idx], o.pl, 0, false) if err != nil { return } ρl = o.res.A_ρl Cpl = o.res.Cpl // compute ρwl. see Eq. (6) of [1] for i := 0; i < o.Ndim; i++ { o.ρwl[i] = 0 for j := 0; j < o.Ndim; j++ { o.ρwl[i] += klr * o.Mdl.Klsat[i][j] * (ρL*o.g[j] - o.gpl[j]) } } // add negative of residual term to fb. see Eqs. (12) and (17) of [1] for m := 0; m < nverts; m++ { r := o.Pmap[m] fb[r] -= coef * S[m] * Cpl * plt for i := 0; i < o.Ndim; i++ { fb[r] += coef * G[m][i] * o.ρwl[i] // += coef * div(ρl*wl) } if o.DoExtrap { // Eq. (19) o.ρl_ex[m] += o.Emat[m][idx] * ρl } } } // contribution from natural boundary conditions if len(o.NatBcs) > 0 { return o.add_natbcs_to_rhs(fb, sol) } return }
// add_surfloads_to_rhs adds surfaces loads to rhs func (o *ElemU) add_surfloads_to_rhs(fb []float64, sol *Solution) (ok bool) { // debugging variables ndim := Global.Ndim if o.Debug { la.VecFill(o.fex, 0) la.VecFill(o.fey, 0) if ndim == 3 { la.VecFill(o.fez, 0) } } // compute surface integral for _, load := range o.NatBcs { for _, ip := range o.IpsFace { if LogErr(o.Shp.CalcAtFaceIp(o.X, ip, load.IdxFace), "add_surfloads_to_rhs") { return } switch load.Key { case "qn", "qn0", "aqn": coef := ip.W * load.Fcn.F(sol.T, nil) * o.Thickness nvec := o.Shp.Fnvec Sf := o.Shp.Sf if Global.Sim.Data.Axisym && load.Key == "aqn" { coef *= o.Shp.AxisymGetRadiusF(o.X, load.IdxFace) } for j, m := range o.Shp.FaceLocalV[load.IdxFace] { for i := 0; i < ndim; i++ { r := o.Umap[i+m*ndim] fb[r] += coef * Sf[j] * nvec[i] // +fe } if o.Debug { o.fex[m] += coef * Sf[j] * nvec[0] o.fey[m] += coef * Sf[j] * nvec[1] if ndim == 3 { o.fez[m] += coef * Sf[j] * nvec[2] } } } } } } return true }
// adds -R to global residual vector fb func (o *Rjoint) AddToRhs(fb []float64, sol *Solution) (err error) { // auxiliary rodH := o.Rod.Cell.Shp rodS := rodH.S rodNn := rodH.Nverts sldH := o.Sld.Cell.Shp sldNn := sldH.Nverts // internal forces vector la.VecFill(o.fC, 0) // loop over rod's integration points var coef, τ, qn1, qn2 float64 for idx, ip := range o.Rod.IpsElem { // auxiliary e0, e1, e2 := o.e0[idx], o.e1[idx], o.e2[idx] // interpolation functions and gradients err = rodH.CalcAtIp(o.Rod.X, ip, true) if err != nil { return } coef = ip[3] * rodH.J // state variables τ = o.States[idx].Sig qn1 = o.States[idx].Phi[0] qn2 = o.States[idx].Phi[1] // fC vector. Eq. (34) for i := 0; i < o.Ndim; i++ { o.qb[i] = τ*o.h*e0[i] + qn1*e1[i] + qn2*e2[i] for m := 0; m < rodNn; m++ { r := i + m*o.Ndim o.fC[r] += coef * rodS[m] * o.qb[i] } } } // fb = -Resid; fR = -fC and fS = Nmat*fC => fb := {fC, -Nmat*fC} for i := 0; i < o.Ndim; i++ { for m := 0; m < rodNn; m++ { r := i + m*o.Ndim I := o.Rod.Umap[r] fb[I] += o.fC[r] // fb := - (fR == -fC Eq (35)) for n := 0; n < sldNn; n++ { s := i + n*o.Ndim J := o.Sld.Umap[s] fb[J] -= o.Nmat[n][m] * o.fC[r] // fb := - (fS Eq (36)) } } } return }
func Assemble(K11, K12 *la.Triplet, F1 []float64, src Cb_src, g *Grid2D, e *Equations) { K11.Start() K12.Start() la.VecFill(F1, 0.0) kx, ky := 1.0, 1.0 alp, bet, gam := 2.0*(kx/g.Dxx+ky/g.Dyy), -kx/g.Dxx, -ky/g.Dyy mol := []float64{alp, bet, bet, gam, gam} for i, I := range e.RF1 { col, row := I%g.Nx, I/g.Nx nodes := []int{I, I - 1, I + 1, I - g.Nx, I + g.Nx} // I, left, right, bottom, top if col == 0 { nodes[1] = nodes[2] } if col == g.Nx-1 { nodes[2] = nodes[1] } if row == 0 { nodes[3] = nodes[4] } if row == g.Ny-1 { nodes[4] = nodes[3] } for k, J := range nodes { j1, j2 := e.FR1[J], e.FR2[J] // 1 or 2? if j1 > -1 { // 11 K11.Put(i, j1, mol[k]) } else { // 12 K12.Put(i, j2, mol[k]) } } if src != nil { x := float64(col) * g.Dx y := float64(row) * g.Dy F1[i] += src(x, y) } } }
// 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 }
// AddToRhs adds -R to global residual vector fb func (o *ElemU) AddToRhs(fb []float64, sol *Solution) (ok bool) { // clear fi vector if using B matrix if o.UseB { la.VecFill(o.fi, 0) } // for each integration point dc := Global.DynCoefs ndim := Global.Ndim nverts := o.Shp.Nverts for idx, ip := range o.IpsElem { // interpolation functions, gradients and variables @ ip if !o.ipvars(idx, sol) { return } // auxiliary coef := o.Shp.J * ip.W * o.Thickness S := o.Shp.S G := o.Shp.G // add internal forces to fb if o.UseB { radius := 1.0 if Global.Sim.Data.Axisym { radius = o.Shp.AxisymGetRadius(o.X) coef *= radius } IpBmatrix(o.B, ndim, nverts, G, radius, S) la.MatTrVecMulAdd(o.fi, coef, o.B, o.States[idx].Sig) // fi += coef * tr(B) * σ } else { for m := 0; m < nverts; m++ { for i := 0; i < ndim; i++ { r := o.Umap[i+m*ndim] for j := 0; j < ndim; j++ { fb[r] -= coef * tsr.M2T(o.States[idx].Sig, i, j) * G[m][j] // -fi } } } } // dynamic term if !Global.Sim.Data.Steady { for m := 0; m < nverts; m++ { for i := 0; i < ndim; i++ { r := o.Umap[i+m*ndim] fb[r] -= coef * S[m] * (o.Rho*(dc.α1*o.us[i]-o.ζs[idx][i]-o.grav[i]) + o.Cdam*(dc.α4*o.us[i]-o.χs[idx][i])) // -RuBar } } } } // assemble fb if using B matrix if o.UseB { for i, I := range o.Umap { fb[I] -= o.fi[i] } } // external forces return o.add_surfloads_to_rhs(fb, sol) }
// adds -R to global residual vector fb func (o ElemUP) AddToRhs(fb []float64, sol *Solution) (ok bool) { // clear variables if o.P.DoExtrap { la.VecFill(o.P.ρl_ex, 0) } if o.U.UseB { la.VecFill(o.U.fi, 0) } // for each integration point dc := Global.DynCoefs ndim := Global.Ndim u_nverts := o.U.Shp.Nverts p_nverts := o.P.Shp.Nverts var coef, plt, klr, ρl, ρ, p, Cpl, Cvs, divvs float64 var r int for idx, ip := range o.U.IpsElem { // interpolation functions, gradients and variables @ ip if !o.ipvars(idx, sol) { return } coef = o.U.Shp.J * ip.W S := o.U.Shp.S G := o.U.Shp.G Sb := o.P.Shp.S Gb := o.P.Shp.G // axisymmetric case radius := 1.0 if Global.Sim.Data.Axisym { radius = o.U.Shp.AxisymGetRadius(o.U.X) coef *= radius } // auxiliary σe := o.U.States[idx].Sig divvs = dc.α4*o.divus - o.U.divχs[idx] // divergence of Eq. (35a) [1] // tpm variables plt = dc.β1*o.P.pl - o.P.ψl[idx] // Eq. (35c) [1] klr = o.P.Mdl.Cnd.Klr(o.P.States[idx].A_sl) if LogErr(o.P.Mdl.CalcLs(o.P.res, o.P.States[idx], o.P.pl, o.divus, false), "AddToRhs") { return } ρl = o.P.res.A_ρl ρ = o.P.res.A_ρ p = o.P.res.A_p Cpl = o.P.res.Cpl Cvs = o.P.res.Cvs // compute ρwl. see Eq (34b) and (35) of [1] for i := 0; i < ndim; i++ { o.P.ρwl[i] = 0 for j := 0; j < ndim; j++ { o.P.ρwl[i] += klr * o.P.Mdl.Klsat[i][j] * o.hl[j] } } // p: add negative of residual term to fb; see Eqs. (38a) and (45a) of [1] for m := 0; m < p_nverts; m++ { r = o.P.Pmap[m] fb[r] -= coef * Sb[m] * (Cpl*plt + Cvs*divvs) for i := 0; i < ndim; i++ { fb[r] += coef * Gb[m][i] * o.P.ρwl[i] // += coef * div(ρl*wl) } if o.P.DoExtrap { // Eq. (19) of [2] o.P.ρl_ex[m] += o.P.Emat[m][idx] * ρl } } // u: add negative of residual term to fb; see Eqs. (38b) and (45b) [1] if o.U.UseB { IpBmatrix(o.U.B, ndim, u_nverts, G, radius, S) la.MatTrVecMulAdd(o.U.fi, coef, o.U.B, σe) // fi += coef * tr(B) * σ for m := 0; m < u_nverts; m++ { for i := 0; i < ndim; i++ { r = o.U.Umap[i+m*ndim] fb[r] -= coef * S[m] * ρ * o.bs[i] fb[r] += coef * p * G[m][i] } } } else { for m := 0; m < u_nverts; m++ { for i := 0; i < ndim; i++ { r = o.U.Umap[i+m*ndim] fb[r] -= coef * S[m] * ρ * o.bs[i] for j := 0; j < ndim; j++ { fb[r] -= coef * tsr.M2T(σe, i, j) * G[m][j] } fb[r] += coef * p * G[m][i] } } } } // add fi term to fb, if using B matrix if o.U.UseB { for i, I := range o.U.Umap { fb[I] -= o.U.fi[i] } } // external forces if len(o.U.NatBcs) > 0 { if !o.U.add_surfloads_to_rhs(fb, sol) { return } } // contribution from natural boundary conditions if len(o.P.NatBcs) > 0 { return o.P.add_natbcs_to_rhs(fb, sol) } return true }
func main() { mpi.Start(false) defer func() { mpi.Stop(false) }() if mpi.Rank() == 0 { chk.PrintTitle("Test ODE 04b (MPI)") io.Pfcyan("Hairer-Wanner VII-p376 Transistor Amplifier (MPI)\n") io.Pfcyan("(from E Hairer's website, not the system in the book)\n") } if mpi.Size() != 3 { chk.Panic(">> error: this test requires 3 MPI processors\n") return } // RIGHT-HAND SIDE OF THE AMPLIFIER PROBLEM w := make([]float64, 8) // workspace fcn := func(f []float64, x float64, y []float64, args ...interface{}) error { d := args[0].(*HWtransData) UET := d.UE * math.Sin(d.W*x) FAC1 := d.BETA * (math.Exp((y[3]-y[2])/d.UF) - 1.0) FAC2 := d.BETA * (math.Exp((y[6]-y[5])/d.UF) - 1.0) la.VecFill(f, 0) switch mpi.Rank() { case 0: f[0] = y[0] / d.R9 case 1: f[1] = (y[1]-d.UB)/d.R8 + d.ALPHA*FAC1 f[2] = y[2]/d.R7 - FAC1 case 2: f[3] = y[3]/d.R5 + (y[3]-d.UB)/d.R6 + (1.0-d.ALPHA)*FAC1 f[4] = (y[4]-d.UB)/d.R4 + d.ALPHA*FAC2 f[5] = y[5]/d.R3 - FAC2 f[6] = y[6]/d.R1 + (y[6]-d.UB)/d.R2 + (1.0-d.ALPHA)*FAC2 f[7] = (y[7] - UET) / d.R0 } mpi.AllReduceSum(f, w) return nil } // JACOBIAN OF THE AMPLIFIER PROBLEM jac := func(dfdy *la.Triplet, x float64, y []float64, args ...interface{}) error { d := args[0].(*HWtransData) FAC14 := d.BETA * math.Exp((y[3]-y[2])/d.UF) / d.UF FAC27 := d.BETA * math.Exp((y[6]-y[5])/d.UF) / d.UF if dfdy.Max() == 0 { dfdy.Init(8, 8, 16) } NU := 2 dfdy.Start() switch mpi.Rank() { case 0: dfdy.Put(2+0-NU, 0, 1.0/d.R9) dfdy.Put(2+1-NU, 1, 1.0/d.R8) dfdy.Put(1+2-NU, 2, -d.ALPHA*FAC14) dfdy.Put(0+3-NU, 3, d.ALPHA*FAC14) dfdy.Put(2+2-NU, 2, 1.0/d.R7+FAC14) case 1: dfdy.Put(1+3-NU, 3, -FAC14) dfdy.Put(2+3-NU, 3, 1.0/d.R5+1.0/d.R6+(1.0-d.ALPHA)*FAC14) dfdy.Put(3+2-NU, 2, -(1.0-d.ALPHA)*FAC14) dfdy.Put(2+4-NU, 4, 1.0/d.R4) dfdy.Put(1+5-NU, 5, -d.ALPHA*FAC27) case 2: dfdy.Put(0+6-NU, 6, d.ALPHA*FAC27) dfdy.Put(2+5-NU, 5, 1.0/d.R3+FAC27) dfdy.Put(1+6-NU, 6, -FAC27) dfdy.Put(2+6-NU, 6, 1.0/d.R1+1.0/d.R2+(1.0-d.ALPHA)*FAC27) dfdy.Put(3+5-NU, 5, -(1.0-d.ALPHA)*FAC27) dfdy.Put(2+7-NU, 7, 1.0/d.R0) } return nil } // MATRIX "M" c1, c2, c3, c4, c5 := 1.0e-6, 2.0e-6, 3.0e-6, 4.0e-6, 5.0e-6 var M la.Triplet M.Init(8, 8, 14) M.Start() NU := 1 switch mpi.Rank() { case 0: M.Put(1+0-NU, 0, -c5) M.Put(0+1-NU, 1, c5) M.Put(2+0-NU, 0, c5) M.Put(1+1-NU, 1, -c5) M.Put(1+2-NU, 2, -c4) M.Put(1+3-NU, 3, -c3) case 1: M.Put(0+4-NU, 4, c3) M.Put(2+3-NU, 3, c3) M.Put(1+4-NU, 4, -c3) case 2: M.Put(1+5-NU, 5, -c2) M.Put(1+6-NU, 6, -c1) M.Put(0+7-NU, 7, c1) M.Put(2+6-NU, 6, c1) M.Put(1+7-NU, 7, -c1) } // WRITE FILE FUNCTION idxstp := 1 var b bytes.Buffer out := func(first bool, dx, x float64, y []float64, args ...interface{}) error { if mpi.Rank() == 0 { if first { fmt.Fprintf(&b, "%6s%23s%23s%23s%23s%23s%23s%23s%23s%23s\n", "ns", "x", "y0", "y1", "y2", "y3", "y4", "y5", "y6", "y7") } fmt.Fprintf(&b, "%6d%23.15E", idxstp, x) for j := 0; j < len(y); j++ { fmt.Fprintf(&b, "%23.15E", y[j]) } fmt.Fprintf(&b, "\n") idxstp += 1 } return nil } defer func() { if mpi.Rank() == 0 { io.WriteFileD("/tmp/gosl", "hwamplifierB.res", &b) } }() // INITIAL DATA D, xa, xb, ya := HWtransIni() // SET ODE SOLVER silent := false fixstp := false //method := "Dopri5" method := "Radau5" ndim := len(ya) //numjac := true numjac := false var osol ode.ODE osol.Pll = true if numjac { osol.Init(method, ndim, fcn, nil, &M, out, silent) } else { osol.Init(method, ndim, fcn, jac, &M, out, silent) } osol.IniH = 1.0e-6 // initial step size // SET TOLERANCES atol, rtol := 1e-11, 1e-5 osol.SetTol(atol, rtol) // RUN t0 := time.Now() if fixstp { osol.Solve(ya, xa, xb, 0.01, fixstp, &D) } else { osol.Solve(ya, xa, xb, xb-xa, fixstp, &D) } if mpi.Rank() == 0 { io.Pfmag("elapsed time = %v\n", time.Now().Sub(t0)) } }
func main() { mpi.Start(false) defer func() { mpi.Stop(false) }() myrank := mpi.Rank() if myrank == 0 { chk.PrintTitle("Test MUMPS Sol 01b") } var t la.Triplet b := []float64{8.0, 45.0, -3.0, 3.0, 19.0} switch mpi.Size() { case 1: t.Init(5, 5, 13) t.Put(0, 0, 1.0) t.Put(0, 0, 1.0) t.Put(1, 0, 3.0) t.Put(0, 1, 3.0) t.Put(2, 1, -1.0) t.Put(4, 1, 4.0) t.Put(1, 2, 4.0) t.Put(2, 2, -3.0) t.Put(3, 2, 1.0) t.Put(4, 2, 2.0) t.Put(2, 3, 2.0) t.Put(1, 4, 6.0) t.Put(4, 4, 1.0) case 2: la.VecFill(b, 0) if myrank == 0 { t.Init(5, 5, 8) t.Put(0, 0, 1.0) t.Put(0, 0, 1.0) t.Put(1, 0, 3.0) t.Put(0, 1, 3.0) t.Put(2, 1, -1.0) t.Put(4, 1, 1.0) t.Put(4, 1, 1.5) t.Put(4, 1, 1.5) b[0] = 8.0 b[1] = 40.0 b[2] = 1.5 } else { t.Init(5, 5, 8) t.Put(1, 2, 4.0) t.Put(2, 2, -3.0) t.Put(3, 2, 1.0) t.Put(4, 2, 2.0) t.Put(2, 3, 2.0) t.Put(1, 4, 6.0) t.Put(4, 4, 0.5) t.Put(4, 4, 0.5) b[1] = 5.0 b[2] = -4.5 b[3] = 3.0 b[4] = 19.0 } default: chk.Panic("this test needs 1 or 2 procs") } x_correct := []float64{1, 2, 3, 4, 5} sum_b_to_root := true la.RunMumpsTestR(&t, 1e-14, b, x_correct, sum_b_to_root) }
// 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 }
// solve_linear_problem solves the linear problem func solve_linear_problem(t float64, d *Domain, dc *DynCoefs, sum *Summary, first bool) (err error) { // 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) // assemble and factorise Jacobian matrix just once if first { // assemble element matrices d.Kb.Start() for _, e := range d.Elems { err = e.AddToKb(d.Kb, d.Sol, true) if err != nil { return } } // join A and tr(A) matrices into Kb if d.Proc == 0 { d.Kb.PutMatAndMatT(&d.EssenBcs.A) } // initialise linear solver (just once) 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 (always if not CteTg) err = d.LinSol.Fact() if err != nil { err = chk.Err("factorisation failed:\n%v", err) return } } // solve for wb 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 } // update Lagrange multipliers (λ) for i := 0; i < d.Nlam; i++ { d.Sol.L[i] += d.Wb[d.Ny+i] // λ += δλ } // update secondary variables for _, e := range d.Elems { err = e.Update(d.Sol) if err != nil { break } } return }
func main() { mpi.Start(false) defer func() { mpi.Stop(false) }() if mpi.Rank() == 0 { chk.PrintTitle("ode04: Hairer-Wanner VII-p376 Transistor Amplifier\n") } if mpi.Size() != 3 { chk.Panic(">> error: this test requires 3 MPI processors\n") return } // data UE, UB, UF, ALPHA, BETA := 0.1, 6.0, 0.026, 0.99, 1.0e-6 R0, R1, R2, R3, R4, R5 := 1000.0, 9000.0, 9000.0, 9000.0, 9000.0, 9000.0 R6, R7, R8, R9 := 9000.0, 9000.0, 9000.0, 9000.0 W := 2.0 * 3.141592654 * 100.0 // initial values xa := 0.0 ya := []float64{0.0, UB, UB / (R6/R5 + 1.0), UB / (R6/R5 + 1.0), UB, UB / (R2/R1 + 1.0), UB / (R2/R1 + 1.0), 0.0} // endpoint of integration xb := 0.05 //xb = 0.0123 // OK //xb = 0.01235 // !OK // right-hand side of the amplifier problem w := make([]float64, 8) // workspace fcn := func(f []float64, dx, x float64, y []float64, args ...interface{}) error { UET := UE * math.Sin(W*x) FAC1 := BETA * (math.Exp((y[3]-y[2])/UF) - 1.0) FAC2 := BETA * (math.Exp((y[6]-y[5])/UF) - 1.0) la.VecFill(f, 0) switch mpi.Rank() { case 0: f[0] = y[0] / R9 case 1: f[1] = (y[1]-UB)/R8 + ALPHA*FAC1 f[2] = y[2]/R7 - FAC1 case 2: f[3] = y[3]/R5 + (y[3]-UB)/R6 + (1.0-ALPHA)*FAC1 f[4] = (y[4]-UB)/R4 + ALPHA*FAC2 f[5] = y[5]/R3 - FAC2 f[6] = y[6]/R1 + (y[6]-UB)/R2 + (1.0-ALPHA)*FAC2 f[7] = (y[7] - UET) / R0 } mpi.AllReduceSum(f, w) return nil } // Jacobian of the amplifier problem jac := func(dfdy *la.Triplet, dx, x float64, y []float64, args ...interface{}) error { FAC14 := BETA * math.Exp((y[3]-y[2])/UF) / UF FAC27 := BETA * math.Exp((y[6]-y[5])/UF) / UF if dfdy.Max() == 0 { dfdy.Init(8, 8, 16) } NU := 2 dfdy.Start() switch mpi.Rank() { case 0: dfdy.Put(2+0-NU, 0, 1.0/R9) dfdy.Put(2+1-NU, 1, 1.0/R8) dfdy.Put(1+2-NU, 2, -ALPHA*FAC14) dfdy.Put(0+3-NU, 3, ALPHA*FAC14) dfdy.Put(2+2-NU, 2, 1.0/R7+FAC14) case 1: dfdy.Put(1+3-NU, 3, -FAC14) dfdy.Put(2+3-NU, 3, 1.0/R5+1.0/R6+(1.0-ALPHA)*FAC14) dfdy.Put(3+2-NU, 2, -(1.0-ALPHA)*FAC14) dfdy.Put(2+4-NU, 4, 1.0/R4) dfdy.Put(1+5-NU, 5, -ALPHA*FAC27) case 2: dfdy.Put(0+6-NU, 6, ALPHA*FAC27) dfdy.Put(2+5-NU, 5, 1.0/R3+FAC27) dfdy.Put(1+6-NU, 6, -FAC27) dfdy.Put(2+6-NU, 6, 1.0/R1+1.0/R2+(1.0-ALPHA)*FAC27) dfdy.Put(3+5-NU, 5, -(1.0-ALPHA)*FAC27) dfdy.Put(2+7-NU, 7, 1.0/R0) } return nil } // matrix "M" c1, c2, c3, c4, c5 := 1.0e-6, 2.0e-6, 3.0e-6, 4.0e-6, 5.0e-6 var M la.Triplet M.Init(8, 8, 14) M.Start() NU := 1 switch mpi.Rank() { case 0: M.Put(1+0-NU, 0, -c5) M.Put(0+1-NU, 1, c5) M.Put(2+0-NU, 0, c5) M.Put(1+1-NU, 1, -c5) M.Put(1+2-NU, 2, -c4) M.Put(1+3-NU, 3, -c3) case 1: M.Put(0+4-NU, 4, c3) M.Put(2+3-NU, 3, c3) M.Put(1+4-NU, 4, -c3) case 2: M.Put(1+5-NU, 5, -c2) M.Put(1+6-NU, 6, -c1) M.Put(0+7-NU, 7, c1) M.Put(2+6-NU, 6, c1) M.Put(1+7-NU, 7, -c1) } // flags silent := false fixstp := false //method := "Dopri5" method := "Radau5" ndim := len(ya) numjac := false // structure to hold numerical results res := ode.Results{Method: method} // ODE solver var osol ode.Solver osol.Pll = true // solve problem if numjac { osol.Init(method, ndim, fcn, nil, &M, ode.SimpleOutput, silent) } else { osol.Init(method, ndim, fcn, jac, &M, ode.SimpleOutput, silent) } osol.IniH = 1.0e-6 // initial step size // set tolerances atol, rtol := 1e-11, 1e-5 osol.SetTol(atol, rtol) // run t0 := time.Now() if fixstp { osol.Solve(ya, xa, xb, 0.01, fixstp, &res) } else { osol.Solve(ya, xa, xb, xb-xa, fixstp, &res) } // plot if mpi.Rank() == 0 { io.Pfmag("elapsed time = %v\n", time.Now().Sub(t0)) plt.SetForEps(2.0, 400) args := "'b-', marker='.', lw=1, clip_on=0" ode.Plot("/tmp/gosl/ode", "hwamplifier_mpi.eps", &res, nil, xa, xb, "", args, func() { _, T, err := io.ReadTable("data/radau5_hwamplifier.dat") if err != nil { chk.Panic("%v", err) } for j := 0; j < ndim; j++ { plt.Subplot(ndim+1, 1, j+1) plt.Plot(T["x"], T[io.Sf("y%d", j)], "'k+',label='reference',ms=10") } }) } }
// AddToRhs adds -R to global residual vector fb func (o *ElemU) AddToRhs(fb []float64, sol *Solution) (err error) { // clear fi vector if using B matrix if o.UseB { la.VecFill(o.fi, 0) } // for each integration point nverts := o.Cell.Shp.Nverts for idx, ip := range o.IpsElem { // interpolation functions, gradients and variables @ ip err = o.ipvars(idx, sol) if err != nil { return } // auxiliary coef := o.Cell.Shp.J * ip[3] * o.Thickness S := o.Cell.Shp.S G := o.Cell.Shp.G // add internal forces to fb if o.UseB { radius := 1.0 if sol.Axisym { radius = o.Cell.Shp.AxisymGetRadius(o.X) coef *= radius } IpBmatrix(o.B, o.Ndim, nverts, G, radius, S, sol.Axisym) la.MatTrVecMulAdd(o.fi, coef, o.B, o.States[idx].Sig) // fi += coef * tr(B) * σ } else { for m := 0; m < nverts; m++ { for i := 0; i < o.Ndim; i++ { r := o.Umap[i+m*o.Ndim] for j := 0; j < o.Ndim; j++ { fb[r] -= coef * tsr.M2T(o.States[idx].Sig, i, j) * G[m][j] // -fi } } } } // dynamic term if !sol.Steady { α1 := sol.DynCfs.α1 α4 := sol.DynCfs.α4 for m := 0; m < nverts; m++ { for i := 0; i < o.Ndim; i++ { r := o.Umap[i+m*o.Ndim] fb[r] -= coef * S[m] * (o.Rho*(α1*o.us[i]-o.ζs[idx][i]-o.grav[i]) + o.Cdam*(α4*o.us[i]-o.χs[idx][i])) // -RuBar } } } } // assemble fb if using B matrix if o.UseB { for i, I := range o.Umap { fb[I] -= o.fi[i] } } // external forces err = o.add_surfloads_to_rhs(fb, sol) if err != nil { return } // contact: additional term to fb err = o.contact_add_to_rhs(fb, sol) // xfem: additional term to fb err = o.xfem_add_to_rhs(fb, sol) return }