func TestJacobian03(tst *testing.T) { //verbose() chk.PrintTitle("TestJacobian 03") // grid var g fdm.Grid2D //g.Init(1.0, 1.0, 4, 4) g.Init(1.0, 1.0, 6, 6) //g.Init(1.0, 1.0, 11, 11) // equations numbering var e fdm.Equations peq := utl.IntUnique(g.L, g.R, g.B, g.T) e.Init(g.N, peq) // K11 and K12 var K11, K12 la.Triplet fdm.InitK11andK12(&K11, &K12, &e) // assembly F1 := make([]float64, e.N1) fdm.Assemble(&K11, &K12, F1, nil, &g, &e) // prescribed values U2 := make([]float64, e.N2) for _, eq := range g.L { U2[e.FR2[eq]] = 50.0 } for _, eq := range g.R { U2[e.FR2[eq]] = 0.0 } for _, eq := range g.B { U2[e.FR2[eq]] = 0.0 } for _, eq := range g.T { U2[e.FR2[eq]] = 50.0 } // functions k11 := K11.ToMatrix(nil) k12 := K12.ToMatrix(nil) ffcn := func(fU1, U1 []float64) error { // K11*U1 + K12*U2 - F1 la.VecCopy(fU1, -1, F1) // fU1 := (-F1) la.SpMatVecMulAdd(fU1, 1, k11, U1) // fU1 += K11*U1 la.SpMatVecMulAdd(fU1, 1, k12, U2) // fU1 += K12*U2 return nil } Jfcn := func(dfU1dU1 *la.Triplet, U1 []float64) error { fdm.Assemble(dfU1dU1, &K12, F1, nil, &g, &e) return nil } U1 := make([]float64, e.N1) CompareJac(tst, ffcn, Jfcn, U1, 0.0075) print_jac := false if print_jac { W1 := make([]float64, e.N1) fU1 := make([]float64, e.N1) ffcn(fU1, U1) var Jnum la.Triplet Jnum.Init(e.N1, e.N1, e.N1*e.N1) Jacobian(&Jnum, ffcn, U1, fU1, W1) la.PrintMat("K11 ", K11.ToMatrix(nil).ToDense(), "%g ", false) la.PrintMat("Jnum", Jnum.ToMatrix(nil).ToDense(), "%g ", false) } test_ffcn := false if test_ffcn { Uc := []float64{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.0, 25.0, 325.0 / 22.0, 100.0 / 11.0, 50.0 / 11.0, 0.0, 50.0, 775.0 / 22.0, 25.0, 375.0 / 22.0, 100.0 / 11.0, 0.0, 50.0, 450.0 / 11.0, 725.0 / 22.0, 25.0, 325.0 / 22.0, 0.0, 50.0, 500.0 / 11.0, 450.0 / 11.0, 775.0 / 22.0, 25.0, 0.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, } for i := 0; i < e.N1; i++ { U1[i] = Uc[e.RF1[i]] } fU1 := make([]float64, e.N1) min, max := la.VecMinMax(fU1) io.Pf("min/max fU1 = %v\n", min, max) } }
// ReadMsh reads a mesh for FE analyses // Note: returns nil on errors func ReadMsh(dir, fn string) *Mesh { // new mesh var o Mesh // read file o.FnamePath = filepath.Join(dir, fn) b, err := io.ReadFile(o.FnamePath) if LogErr(err, "msh: cannot open mesh file "+o.FnamePath) { return nil } // decode if LogErr(json.Unmarshal(b, &o), "msh: cannot unmarshal mesh file "+fn+"\n") { return nil } // check if LogErrCond(len(o.Verts) < 2, "msh: mesh must have at least 2 vertices and 1 cell") { return nil } if LogErrCond(len(o.Cells) < 1, "msh: mesh must have at least 2 vertices and 1 cell") { return nil } // vertex related derived data o.Ndim = 2 o.Xmin = o.Verts[0].C[0] o.Ymin = o.Verts[0].C[1] if len(o.Verts[0].C) > 2 { o.Zmin = o.Verts[0].C[2] } o.Xmax = o.Xmin o.Ymax = o.Ymin o.Zmax = o.Zmin o.VertTag2verts = make(map[int][]*Vert) for i, v := range o.Verts { // check vertex id if LogErrCond(v.Id != i, "msh: vertices must be sequentially numbered. %d != %d\n", v.Id, i) { return nil } // ndim nd := len(v.C) if LogErrCond(nd < 2 || nd > 4, "msh: ndim must be 2 or 3\n") { return nil } if nd == 3 { if math.Abs(v.C[2]) > Ztol { o.Ndim = 3 } } // tags if v.Tag < 0 { verts := o.VertTag2verts[v.Tag] o.VertTag2verts[v.Tag] = append(verts, v) } // limits o.Xmin = min(o.Xmin, v.C[0]) o.Xmax = max(o.Xmax, v.C[0]) o.Ymin = min(o.Ymin, v.C[1]) o.Ymax = max(o.Ymax, v.C[1]) if nd > 2 { o.Zmin = min(o.Zmin, v.C[2]) o.Zmax = max(o.Zmax, v.C[2]) } } // derived data o.CellTag2cells = make(map[int][]*Cell) o.FaceTag2cells = make(map[int][]CellFaceId) o.FaceTag2verts = make(map[int][]int) o.SeamTag2cells = make(map[int][]CellSeamId) o.Ctype2cells = make(map[string][]*Cell) o.Part2cells = make(map[int][]*Cell) for i, c := range o.Cells { // check id and tag if LogErrCond(c.Id != i, "msh: cells must be sequentially numbered. %d != %d\n", c.Id, i) { return nil } if LogErrCond(c.Tag >= 0, "msh: cell tags must be negative\n") { return nil } // face tags cells := o.CellTag2cells[c.Tag] o.CellTag2cells[c.Tag] = append(cells, c) for i, ftag := range c.FTags { if ftag < 0 { pairs := o.FaceTag2cells[ftag] o.FaceTag2cells[ftag] = append(pairs, CellFaceId{c, i}) for _, l := range shp.GetFaceLocalVerts(c.Type, i) { utl.IntIntsMapAppend(&o.FaceTag2verts, ftag, o.Verts[c.Verts[l]].Id) } } } // seam tags if o.Ndim == 3 { for i, stag := range c.STags { if stag < 0 { pairs := o.SeamTag2cells[stag] o.SeamTag2cells[stag] = append(pairs, CellSeamId{c, i}) } } } // cell type => cells cells = o.Ctype2cells[c.Type] o.Ctype2cells[c.Type] = append(cells, c) // partition => cells cells = o.Part2cells[c.Part] o.Part2cells[c.Part] = append(cells, c) // get shape structure switch c.Type { case "joint": c.IsJoint = true default: c.Shp = shp.Get(c.Type) if LogErrCond(c.Shp == nil, "msh: cannot find shape type == %q\n", c.Type) { return nil } } } // remove duplicates for ftag, verts := range o.FaceTag2verts { o.FaceTag2verts[ftag] = utl.IntUnique(verts) } // log log.Printf("msh: fn=%s nverts=%d ncells=%d ncelltags=%d nfacetags=%d nseamtags=%d nverttags=%d ncelltypes=%d npart=%d\n", fn, len(o.Verts), len(o.Cells), len(o.CellTag2cells), len(o.FaceTag2cells), len(o.SeamTag2cells), len(o.VertTag2verts), len(o.Ctype2cells), len(o.Part2cells)) return &o }
func Test_nls04(tst *testing.T) { //verbose() chk.PrintTitle("nls04. finite differences problem") // grid var g fdm.Grid2D g.Init(1.0, 1.0, 6, 6) // equations numbering var e fdm.Equations peq := utl.IntUnique(g.L, g.R, g.B, g.T) e.Init(g.N, peq) // K11 and K12 var K11, K12 la.Triplet fdm.InitK11andK12(&K11, &K12, &e) // assembly F1 := make([]float64, e.N1) fdm.Assemble(&K11, &K12, F1, nil, &g, &e) // prescribed values U2 := make([]float64, e.N2) for _, eq := range g.L { U2[e.FR2[eq]] = 50.0 } for _, eq := range g.R { U2[e.FR2[eq]] = 0.0 } for _, eq := range g.B { U2[e.FR2[eq]] = 0.0 } for _, eq := range g.T { U2[e.FR2[eq]] = 50.0 } // functions k11 := K11.ToMatrix(nil) k12 := K12.ToMatrix(nil) ffcn := func(fU1, U1 []float64) error { // K11*U1 + K12*U2 - F1 la.VecCopy(fU1, -1, F1) // fU1 := (-F1) la.SpMatVecMulAdd(fU1, 1, k11, U1) // fU1 += K11*U1 la.SpMatVecMulAdd(fU1, 1, k12, U2) // fU1 += K12*U2 return nil } Jfcn := func(dfU1dU1 *la.Triplet, U1 []float64) error { fdm.Assemble(dfU1dU1, &K12, F1, nil, &g, &e) return nil } JfcnD := func(dfU1dU1 [][]float64, U1 []float64) error { la.MatCopy(dfU1dU1, 1, K11.ToMatrix(nil).ToDense()) return nil } prms := map[string]float64{ "atol": 1e-8, "rtol": 1e-8, "ftol": 1e-12, "lSearch": 0.0, } // init var nls_sps NlSolver // sparse analytical var nls_num NlSolver // sparse numerical var nls_den NlSolver // dense analytical nls_sps.Init(e.N1, ffcn, Jfcn, nil, false, false, prms) nls_num.Init(e.N1, ffcn, nil, nil, false, true, prms) nls_den.Init(e.N1, ffcn, nil, JfcnD, true, false, prms) defer nls_sps.Clean() defer nls_num.Clean() defer nls_den.Clean() // results U1sps := make([]float64, e.N1) U1num := make([]float64, e.N1) U1den := make([]float64, e.N1) Usps := make([]float64, e.N) Unum := make([]float64, e.N) Uden := make([]float64, e.N) // solution Uc := []float64{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.0, 25.0, 325.0 / 22.0, 100.0 / 11.0, 50.0 / 11.0, 0.0, 50.0, 775.0 / 22.0, 25.0, 375.0 / 22.0, 100.0 / 11.0, 0.0, 50.0, 450.0 / 11.0, 725.0 / 22.0, 25.0, 325.0 / 22.0, 0.0, 50.0, 500.0 / 11.0, 450.0 / 11.0, 775.0 / 22.0, 25.0, 0.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, } io.PfYel("\n---- sparse -------- Analytical Jacobian -------------------\n") // solve err := nls_sps.Solve(U1sps, false) if err != nil { chk.Panic(err.Error()) } // check fdm.JoinVecs(Usps, U1sps, U2, &e) chk.Vector(tst, "Usps", 1e-14, Usps, Uc) // plot if false { g.Contour("results", "fig_t_heat_square", nil, Usps, 11, false) } io.PfYel("\n---- dense -------- Analytical Jacobian -------------------\n") // solve err = nls_den.Solve(U1den, false) if err != nil { chk.Panic(err.Error()) } // check fdm.JoinVecs(Uden, U1den, U2, &e) chk.Vector(tst, "Uden", 1e-14, Uden, Uc) io.PfYel("\n---- sparse -------- Numerical Jacobian -------------------\n") // solve err = nls_num.Solve(U1num, false) if err != nil { chk.Panic(err.Error()) } // check fdm.JoinVecs(Unum, U1num, U2, &e) chk.Vector(tst, "Unum", 1e-14, Unum, Uc) }
// ReadMsh reads a mesh for FE analyses // Note: returns nil on errors func ReadMsh(dir, fn string, goroutineId int) (o *Mesh, err error) { // new mesh o = new(Mesh) // read file o.FnamePath = filepath.Join(dir, fn) b, err := io.ReadFile(o.FnamePath) if err != nil { return } // decode err = json.Unmarshal(b, &o) if err != nil { return } // check if len(o.Verts) < 2 { err = chk.Err("at least 2 vertices are required in mesh\n") return } if len(o.Cells) < 1 { err = chk.Err("at least 1 cell is required in mesh\n") return } // variables for NURBS var controlpts [][]float64 has_nurbs := false if len(o.Nurbss) > 0 { controlpts = make([][]float64, len(o.Verts)) has_nurbs = true } // vertex related derived data o.Ndim = 2 o.Xmin = o.Verts[0].C[0] o.Ymin = o.Verts[0].C[1] if len(o.Verts[0].C) > 2 { o.Zmin = o.Verts[0].C[2] } o.Xmax = o.Xmin o.Ymax = o.Ymin o.Zmax = o.Zmin o.VertTag2verts = make(map[int][]*Vert) for i, v := range o.Verts { // check vertex id if v.Id != i { err = chk.Err("vertices ids must coincide with order in \"verts\" list. %d != %d\n", v.Id, i) return } // ndim nd := len(v.C) if nd < 2 || nd > 4 { err = chk.Err("number of space dimensions must be 2, 3 or 4 (NURBS). %d is invalid\n", nd) return } if nd == 3 { if math.Abs(v.C[2]) > Ztol { o.Ndim = 3 } } // tags if v.Tag < 0 { verts := o.VertTag2verts[v.Tag] o.VertTag2verts[v.Tag] = append(verts, v) } // limits o.Xmin = utl.Min(o.Xmin, v.C[0]) o.Xmax = utl.Max(o.Xmax, v.C[0]) o.Ymin = utl.Min(o.Ymin, v.C[1]) o.Ymax = utl.Max(o.Ymax, v.C[1]) if nd > 2 { o.Zmin = utl.Min(o.Zmin, v.C[2]) o.Zmax = utl.Max(o.Zmax, v.C[2]) } // control points to initialise NURBS if has_nurbs { controlpts[i] = make([]float64, 4) for j := 0; j < 4; j++ { controlpts[i][j] = v.C[j] } } } // allocate NURBSs o.PtNurbs = make([]*gm.Nurbs, len(o.Nurbss)) o.NrbFaces = make([][]*gm.Nurbs, len(o.Nurbss)) for i, d := range o.Nurbss { o.PtNurbs[i] = new(gm.Nurbs) o.PtNurbs[i].Init(d.Gnd, d.Ords, d.Knots) o.PtNurbs[i].SetControl(controlpts, d.Ctrls) o.NrbFaces[i] = o.PtNurbs[i].ExtractSurfaces() } // derived data o.CellTag2cells = make(map[int][]*Cell) o.FaceTag2cells = make(map[int][]CellFaceId) o.FaceTag2verts = make(map[int][]int) o.SeamTag2cells = make(map[int][]CellSeamId) o.Ctype2cells = make(map[string][]*Cell) o.Part2cells = make(map[int][]*Cell) for i, c := range o.Cells { // check id and tag if c.Id != i { err = chk.Err("cells ids must coincide with order in \"verts\" list. %d != %d\n", c.Id, i) return } if c.Tag >= 0 { err = chk.Err("cells tags must be negative. %d is incorrect\n", c.Tag) return } // get shape structure switch c.Type { case "joint": c.IsJoint = true case "nurbs": c.Shp = shp.GetShapeNurbs(o.PtNurbs[c.Nrb], o.NrbFaces[c.Nrb], c.Span) if c.Shp == nil { err = chk.Err("cannot allocate \"shape\" structure for cell type = %q\n", c.Type) return } default: c.Shp = shp.Get(c.Type, goroutineId) if c.Shp == nil { err = chk.Err("cannot allocate \"shape\" structure for cell type = %q\n", c.Type) return } } c.GoroutineId = goroutineId // face tags cells := o.CellTag2cells[c.Tag] o.CellTag2cells[c.Tag] = append(cells, c) for i, ftag := range c.FTags { if ftag < 0 { pairs := o.FaceTag2cells[ftag] o.FaceTag2cells[ftag] = append(pairs, CellFaceId{c, i}) for _, l := range c.Shp.FaceLocalVerts[i] { utl.IntIntsMapAppend(&o.FaceTag2verts, ftag, o.Verts[c.Verts[l]].Id) } } } // seam tags if o.Ndim == 3 { for i, stag := range c.STags { if stag < 0 { pairs := o.SeamTag2cells[stag] o.SeamTag2cells[stag] = append(pairs, CellSeamId{c, i}) } } } // cell type => cells cells = o.Ctype2cells[c.Type] o.Ctype2cells[c.Type] = append(cells, c) // partition => cells cells = o.Part2cells[c.Part] o.Part2cells[c.Part] = append(cells, c) } // remove duplicates for ftag, verts := range o.FaceTag2verts { o.FaceTag2verts[ftag] = utl.IntUnique(verts) } // results return }