Example #1
0
// 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
}
Example #2
0
// SetStage set nodes, equation numbers and auxiliary data for given stage
func (o *Domain) SetStage(idxstg int, stg *inp.Stage, distr bool) (setstageisok bool) {

	// backup state
	if idxstg > 0 {
		o.create_stage_copy()
		if !o.fix_inact_flags(stg.Activate, false) {
			return
		}
		if !o.fix_inact_flags(stg.Deactivate, true) {
		}
	}

	// auxiliary maps for setting boundary conditions
	o.FaceConds = make(map[int][]*FaceCond) // cid => conditions

	// nodes (active) and elements (active AND in this processor)
	o.Nodes = make([]*Node, 0)
	o.Elems = make([]Elem, 0)
	o.MyCids = make([]int, 0)

	// auxiliary maps for dofs and equation types
	o.F2Y = make(map[string]string)
	o.YandC = GetIsEssenKeyMap()
	o.Dof2Tnum = make(map[string]int)

	// auxiliary maps for nodes and elements
	o.Vid2node = make([]*Node, len(o.Msh.Verts))
	o.Cid2elem = make([]Elem, len(o.Msh.Cells))
	o.Cid2active = make([]bool, len(o.Msh.Cells))

	// subsets of elements
	o.ElemConnect = make([]ElemConnector, 0)
	o.ElemIntvars = make([]ElemIntvars, 0)

	// allocate nodes and cells (active only) -------------------------------------------------------

	// for each cell
	var eq int // current equation number => total number of equations @ end of loop
	o.NnzKb = 0
	for _, c := range o.Msh.Cells {

		// get element data and information structure
		edat := o.Reg.Etag2data(c.Tag)
		if LogErrCond(edat == nil, "cannot get element's data with etag=%d", c.Tag) {
			return
		}
		if edat.Inact {
			continue
		}
		o.Cid2active[c.Id] = true

		// prepare maps of face conditions
		for faceId, faceTag := range c.FTags {
			if faceTag < 0 {
				faceBc := stg.GetFaceBc(faceTag)
				if faceBc != nil {
					lverts := shp.GetFaceLocalVerts(c.Type, faceId)
					gverts := o.faceLocal2globalVerts(lverts, c)
					for j, key := range faceBc.Keys {
						fcn := Global.Sim.Functions.Get(faceBc.Funcs[j])
						if LogErrCond(fcn == nil, "cannot find function named %q corresponding to face tag %d (@ element %d)", faceBc.Funcs[j], faceTag, c.Id) {
							return
						}
						fcond := &FaceCond{faceId, lverts, gverts, key, fcn, faceBc.Extra}
						fconds := o.FaceConds[c.Id]
						o.FaceConds[c.Id] = append(fconds, fcond)
					}
				}
			}
		}

		// get element info (such as DOFs, etc.)
		info := GetElemInfo(c.Type, edat.Type, o.FaceConds[c.Id])
		if info == nil {
			return
		}
		// for non-joint elements, add new DOFs
		if !c.IsJoint {
			chk.IntAssert(len(info.Dofs), len(c.Verts))

			// store y and f information
			for ykey, fkey := range info.Y2F {
				o.F2Y[fkey] = ykey
				o.YandC[ykey] = true
			}

			// t1 and t2 equations
			for _, ykey := range info.T1vars {
				o.Dof2Tnum[ykey] = 1
			}
			for _, ykey := range info.T2vars {
				o.Dof2Tnum[ykey] = 2
			}

			// loop over nodes of this element
			var eNdof int // number of DOFs of this elmeent
			for j, v := range c.Verts {

				// new or existent node
				var nod *Node
				if o.Vid2node[v] == nil {
					nod = NewNode(o.Msh.Verts[v])
					o.Vid2node[v] = nod
					o.Nodes = append(o.Nodes, nod)
				} else {
					nod = o.Vid2node[v]
				}

				// set DOFs and equation numbers
				for _, ukey := range info.Dofs[j] {
					eq = nod.AddDofAndEq(ukey, eq)
					eNdof += 1
				}
			}

			// number of non-zeros
			o.NnzKb += eNdof * eNdof
		}

		// allocate element
		mycell := c.Part == Global.Rank // cell belongs to this processor
		if !distr {
			mycell = true // not distributed simulation => this processor has all cells
		}
		if mycell {

			// new element
			ele := NewElem(edat, c.Id, o.Msh, o.FaceConds[c.Id])
			if ele == nil {
				return
			}
			o.Cid2elem[c.Id] = ele
			o.Elems = append(o.Elems, ele)
			o.MyCids = append(o.MyCids, ele.Id())

			// give equation numbers to new element
			eqs := make([][]int, len(c.Verts))
			for j, v := range c.Verts {
				for _, dof := range o.Vid2node[v].Dofs {
					eqs[j] = append(eqs[j], dof.Eq)
				}
			}
			if LogErrCond(!ele.SetEqs(eqs, nil), "cannot set element equations") {
				return
			}

			// subsets of elements
			o.add_element_to_subsets(ele)
		}
	}

	// connect elements (e.g. Joints)
	for _, e := range o.ElemConnect {
		nnz, ok := e.Connect(o.Cid2elem, o.Msh.Cells[e.Id()])
		if LogErrCond(!ok, "Connect failed") {
			return
		}
		o.NnzKb += nnz
	}

	// logging
	log.Printf("dom: stage # %d %s\n", idxstg, stg.Desc)
	log.Printf("dom: nnodes=%d nelems=%d\n", len(o.Nodes), len(o.Elems))

	// element conditions, essential and natural boundary conditions --------------------------------

	// (re)set constraints and prescribed forces structures
	o.EssenBcs.Reset()
	o.PtNatBcs.Reset()

	// element conditions
	for _, ec := range stg.EleConds {
		cells, ok := o.Msh.CellTag2cells[ec.Tag]
		if LogErrCond(!ok, "cannot find cells with tag = %d to assign conditions", ec.Tag) {
			return
		}
		for _, c := range cells {
			e := o.Cid2elem[c.Id]
			if e != nil { // set conditions only for this processor's / active element
				for j, key := range ec.Keys {
					fcn := Global.Sim.Functions.Get(ec.Funcs[j])
					if LogErrCond(fcn == nil, "Functions.Get failed\n") {
						return
					}
					e.SetEleConds(key, fcn, ec.Extra)
				}
			}
		}
	}

	// face boundary conditions
	for cidx, fcs := range o.FaceConds {
		c := o.Msh.Cells[cidx]
		for _, fc := range fcs {
			lverts := fc.LocalVerts
			gverts := o.faceLocal2globalVerts(lverts, c)
			var enodes []*Node
			for _, v := range gverts {
				enodes = append(enodes, o.Vid2node[v])
			}
			if o.YandC[fc.Cond] {
				if !o.EssenBcs.Set(fc.Cond, enodes, fc.Func, fc.Extra) {
					return
				}
			}

		}
	}

	// vertex bounday conditions
	for _, nc := range stg.NodeBcs {
		verts, ok := o.Msh.VertTag2verts[nc.Tag]
		if LogErrCond(!ok, "cannot find vertices with tag = %d to assign node boundary conditions", nc.Tag) {
			return
		}
		for _, v := range verts {
			if o.Vid2node[v.Id] != nil { // set BCs only for active nodes
				n := o.Vid2node[v.Id]
				for j, key := range nc.Keys {
					fcn := Global.Sim.Functions.Get(nc.Funcs[j])
					if LogErrCond(fcn == nil, "Functions.Get failed\n") {
						return
					}
					if o.YandC[key] {
						o.EssenBcs.Set(key, []*Node{n}, fcn, nc.Extra)
					} else {
						o.PtNatBcs.Set(o.F2Y[key], n, fcn, nc.Extra)
					}
				}
			}
		}
	}

	// resize slices --------------------------------------------------------------------------------

	// t1 and t2 equations
	o.T1eqs = make([]int, 0)
	o.T2eqs = make([]int, 0)
	for _, nod := range o.Nodes {
		for _, dof := range nod.Dofs {
			switch o.Dof2Tnum[dof.Key] {
			case 1:
				o.T1eqs = append(o.T1eqs, dof.Eq)
			case 2:
				o.T2eqs = append(o.T2eqs, dof.Eq)
			default:
				LogErrCond(true, "t1 and t2 equations are incorrectly set")
				return
			}
		}
	}

	// size of arrays
	o.Ny = eq
	o.Nlam, o.NnzA = o.EssenBcs.Build(o.Ny)
	o.Nyb = o.Ny + o.Nlam

	// solution structure and linear solver
	o.Sol = new(Solution)
	o.Kb = new(la.Triplet)
	o.Fb = make([]float64, o.Nyb)
	o.Wb = make([]float64, o.Nyb)
	o.Kb.Init(o.Nyb, o.Nyb, o.NnzKb+2*o.NnzA)
	o.InitLSol = true // tell solver that lis has to be initialised before use

	// allocate arrays
	o.Sol.Y = make([]float64, o.Ny)
	o.Sol.ΔY = make([]float64, o.Ny)
	o.Sol.L = make([]float64, o.Nlam)
	if !Global.Sim.Data.Steady {
		o.Sol.Dydt = make([]float64, o.Ny)
		o.Sol.D2ydt2 = make([]float64, o.Ny)
		o.Sol.Psi = make([]float64, o.Ny)
		o.Sol.Zet = make([]float64, o.Ny)
		o.Sol.Chi = make([]float64, o.Ny)
	}

	// initialise internal variables
	if stg.HydroSt {
		if !o.SetHydroSt(stg) {
			return
		}
	} else if stg.GeoSt != nil {
		if !o.SetGeoSt(stg) {
			return
		}
	} else if stg.IniStress != nil {
		if !o.SetIniStress(stg) {
			return
		}
	} else {
		for _, e := range o.ElemIntvars {
			e.SetIniIvs(o.Sol, nil)
		}
	}

	// import results from another set of files
	if stg.Import != nil {
		sum := ReadSum(stg.Import.Dir, stg.Import.Fnk)
		if LogErrCond(sum == nil, "cannot import state from %s/%s.sim", stg.Import.Dir, stg.Import.Fnk) {
			return
		}
		if !o.In(sum, len(sum.OutTimes)-1, false) {
			return
		}
		if LogErrCond(o.Ny != len(o.Sol.Y), "import failed: length of primary variables vector imported is not equal to the one allocated. make sure the number of DOFs of the imported simulation matches this one. %d != %d", o.Ny, len(o.Sol.Y)) {
			return
		}
		if stg.Import.ResetU {
			for _, ele := range o.ElemIntvars {
				if LogErrCond(!ele.Ureset(o.Sol), "cannot run reset function of element after displacements are zeroed") {
					return
				}
			}
			for _, nod := range o.Nodes {
				for _, ukey := range []string{"ux", "uy", "uz"} {
					eq := nod.GetEq(ukey)
					if eq >= 0 {
						o.Sol.Y[eq] = 0
						if len(o.Sol.Dydt) > 0 {
							o.Sol.Dydt[eq] = 0
							o.Sol.D2ydt2[eq] = 0
						}
					}
				}
			}
		}
	}

	// logging
	if Global.LogBcs {
		log.Printf("dom: essential boundary conditions:%v", o.EssenBcs.List(stg.Control.Tf))
		log.Printf("dom: ptnatbcs=%v", o.PtNatBcs.List(stg.Control.Tf))
	}
	log.Printf("dom: ny=%d nlam=%d nnzKb=%d nnzA=%d nt1eqs=%d nt2eqs=%d", o.Ny, o.Nlam, o.NnzKb, o.NnzA, len(o.T1eqs), len(o.T2eqs))

	// success
	return true
}
Example #3
0
// NodesOnPlane finds vertices located on {x,y,z} plane and returns an iterator
// that can be used to extract results on the plane. An {u,v}-coordinates system is built
// on the plane.
//  Input:
//    ftag -- cells' face tag; e.g. -31
//  Output:
//    dat -- plane data holding vertices on plane and other information; see above
//  Note:
//    1) this function works with 3D cells only
//    2) faces on cells must be either "qua4" or "qua8" types
//    3) middle nodes in "qua8" are disregarded in order to buidl an {u,v} grid
//    4) the resulting mesh on plane must be equally spaced; i.e. Δx and Δy are constant;
//       but not necessarily equal to each other
func NodesOnPlane(ftag int) *PlaneData {

	// check
	ndim := Dom.Msh.Ndim
	if ndim != 3 {
		chk.Panic("this function works in 3D only")
	}

	// loop over cells on face with given face tag
	var dat PlaneData
	dat.Plane = -1
	dat.Ids = make(map[int]bool)
	dat.Dx = make([]float64, ndim)
	Δx := make([]float64, ndim)
	first := true
	cells := Dom.Msh.FaceTag2cells[ftag]
	for _, cell := range cells {
		ctype := cell.C.Type
		for fidx, cftag := range cell.C.FTags {
			if cftag == ftag {

				// check face type
				ftype := shp.GetFaceType(ctype)
				if !(ftype == "qua4" || ftype == "qua8") {
					chk.Panic("can only handle qua4 or qua8 faces for now. ftype=%q", ftype)
				}

				// vertices on face
				flvids := shp.GetFaceLocalVerts(ctype, fidx)
				nv := len(flvids)
				if nv == 8 {
					nv = 4 // avoid middle nodes
				}
				for i := 0; i < nv; i++ {
					vid := cell.C.Verts[flvids[i]]
					dat.Ids[vid] = true
				}

				// compute and check increments in global coordinates
				for i := 1; i < nv; i++ {
					a := cell.C.Verts[flvids[i]]
					b := cell.C.Verts[flvids[i-1]]
					xa := Dom.Msh.Verts[a].C
					xb := Dom.Msh.Verts[b].C
					for j := 0; j < ndim; j++ {
						Δx[j] = max(Δx[j], math.Abs(xa[j]-xb[j]))
					}
				}
				if first {
					for j := 0; j < ndim; j++ {
						dat.Dx[j] = Δx[j]
					}
					first = false
				} else {
					for j := 0; j < ndim; j++ {
						if math.Abs(dat.Dx[j]-Δx[j]) > 1e-10 {
							chk.Panic("all faces must have the same Δx,Δy,Δz")
						}
					}
				}

				// find which plane vertices are located on => which plane this face is parallel to
				perpto := -1 // "perpendicular to" indicator
				switch {
				case Δx[0] < DIST_TOL: // plane perpendicular to x-axis
					perpto = 0
				case Δx[1] < DIST_TOL: // plane perpendicular to y-axis
					perpto = 1
				case Δx[2] < DIST_TOL: // plane perpendicular to z-axis
					perpto = 2
				default:
					chk.Panic("planes must be perpendicular to one of the x-y-z axes")
				}
				if dat.Plane < 0 {
					dat.Plane = perpto
				} else {
					if perpto != dat.Plane {
						chk.Panic("all planes must be perperdicular to the same axis")
					}
				}
			}
		}
	}

	// uv: indices and increments
	dat.Iu = make([]int, 2)
	dat.Du = make([]float64, 2)
	switch dat.Plane {
	case 0: // plane perpendicular to x-axis
		dat.Iu = []int{1, 2}
	case 1: // plane perpendicular to y-axis
		dat.Iu = []int{0, 2}
	case 2: // plane perpendicular to z-axis
		dat.Iu = []int{0, 1}
	}
	for j := 0; j < 2; j++ {
		dat.Du[j] = dat.Dx[dat.Iu[j]]
	}

	// uv: limits
	dat.Umin = make([]float64, 2)
	dat.Umax = make([]float64, 2)
	first = true
	for vid, _ := range dat.Ids {
		x := Dom.Msh.Verts[vid].C
		if first {
			for j := 0; j < 2; j++ {
				dat.Umin[j] = x[dat.Iu[j]]
				dat.Umax[j] = x[dat.Iu[j]]
			}
			first = false
		} else {
			for j := 0; j < 2; j++ {
				dat.Umin[j] = min(dat.Umin[j], x[dat.Iu[j]])
				dat.Umax[j] = max(dat.Umax[j], x[dat.Iu[j]])
			}
		}
	}

	// uv: size of grid
	dat.Nu = make([]int, 2)
	for j := 0; j < 2; j++ {
		dat.Nu[j] = int((dat.Umax[j]-dat.Umin[j])/dat.Du[j]) + 1
	}

	// uv: bins
	dd := DIST_TOL * 2
	nb := 20
	dat.Ubins.Init([]float64{dat.Umin[0] - dd, dat.Umin[1] - dd}, []float64{dat.Umax[0] + dd, dat.Umax[1] + dd}, nb)
	u := []float64{0, 0}
	for vid, _ := range dat.Ids {
		x := Dom.Msh.Verts[vid].C
		for j := 0; j < 2; j++ {
			u[j] = x[dat.Iu[j]]
		}
		err := dat.Ubins.Append(u, vid)
		if err != nil {
			chk.Panic("cannot append {u,v} coordinate to bins. u=%v", u)
		}
	}

	// allocate F
	dat.F = la.MatAlloc(dat.Nu[0], dat.Nu[1])
	return &dat
}