コード例 #1
0
ファイル: plotter.go プロジェクト: PaddySchmidt/gofem
func (o *Plotter) Plot_Dgam_f(x, y []float64, res []*State, sts [][]float64, last bool) {
	if o.m == nil {
		o.set_empty()
		return
	}
	nr := len(res)
	k := nr - 1
	ys := o.m.YieldFuncs(res[0])
	fc0 := ys[0]
	xmi, xma, ymi, yma := res[0].Dgam, res[0].Dgam, fc0, fc0
	for i := 0; i < nr; i++ {
		x[i] = res[i].Dgam
		ys = o.m.YieldFuncs(res[i])
		y[i] = ys[0]
		xmi = utl.Min(xmi, x[i])
		xma = utl.Max(xma, x[i])
		ymi = utl.Min(ymi, y[i])
		yma = utl.Max(yma, y[i])
	}
	//o.DrawRamp(xmi, xma, ymi, yma)
	plt.Plot(x, y, io.Sf("'r.', ls='%s', clip_on=0, color='%s', marker='%s', label=r'%s'", o.Ls, o.Clr, o.Mrk, o.Lbl))
	plt.PlotOne(x[0], y[0], io.Sf("'bo', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.SpMrk, o.SpMs))
	plt.PlotOne(x[k], y[k], io.Sf("'bs', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.EpMrk, o.EpMs))
	if last {
		plt.Gll("$\\Delta\\gamma$", "$f$", "")
		if lims, ok := o.Lims["Dgam,f"]; ok {
			plt.AxisLims(lims)
		}
	}
}
コード例 #2
0
ファイル: refm1.go プロジェクト: PaddySchmidt/gofem
func (o *RefM1) wetting(x, y float64) {
	o.Dw = utl.Max(o.y0-y, 0.0)
	o.λwb = o.λw * (1.0 - math.Exp(-o.βw*o.Dw))
	o.yw = -o.λw*x - math.Log(o.c3w+o.c2w*math.Exp(o.c1w*x))/o.βw
	o.D = utl.Max(y-o.yw, 0.0)
	o.λb = o.λwb * math.Exp(-o.β1*o.D)
	return
}
コード例 #3
0
ファイル: refm1.go プロジェクト: PaddySchmidt/gofem
func (o *RefM1) drying(x, y float64) {
	o.Dd = utl.Max(y-o.yr, 0.0)
	o.λdb = o.λd * (1.0 - math.Exp(-o.βd*o.Dd))
	o.yd = -o.λd*x + math.Log(o.c3d+o.c2d*math.Exp(o.c1d*x))/o.βd
	o.D = utl.Max(o.yd-y, 0.0)
	o.β2b = o.β2 * math.Pow(utl.Max(y, 0.0), o.α)
	o.λb = o.λdb * math.Exp(-o.β2b*o.D)
	return
}
コード例 #4
0
ファイル: msh.go プロジェクト: PaddySchmidt/gofem
// CalcScaleFactor computes scale factor based on mesh dimensions
//  Input:
//   Y    -- results; e.g. bending moments
//   coef -- coefficient to scale max(dimension) divided by max(Y); e.g. 0.1
//  Output:
//   sf -- scaling factor
func (o *Mesh) CalcScaleFactor(Y []float64, coef float64) (sf float64) {
	if len(Y) < 2 {
		chk.Panic("at least two values are required in Y in order to compute scaling factor")
	}
	dx := o.Xmax - o.Xmin
	dy := o.Ymax - o.Ymin
	d := utl.Max(dx, dy)
	Ymax := math.Abs(Y[0])
	for i := 1; i < len(Y); i++ {
		Ymax = utl.Max(Ymax, Y[i])
	}
	return coef * d / Ymax
}
コード例 #5
0
ファイル: output.go プロジェクト: PaddySchmidt/gofem
// PlotAllBendingMoments plots all bending moments
//  Input:
//   dom       -- Domain
//   nstations -- number of stations
//   withtext  -- show bending moment values
//   numfmt    -- number format for values
//   tolM      -- tolerance to clip absolute values of M
//   coef      -- coefficient to scale max(dimension) divided by max(Y); e.g. 0.1
//  Output:
//   beams -- all beam elements
//   allM  -- bending moments corresponding to all beams
func PlotAllBendingMoments(dom *Domain, nstations int, withtext bool, numfmt string, tolM, coef float64) (beams []*Beam, allM [][]float64) {

	// collect beams
	for _, elem := range dom.Elems {
		if beam, ok := elem.(*Beam); ok {
			beams = append(beams, beam)
		}
	}

	// compute bending moments
	allM = make([][]float64, len(beams))
	for i, beam := range beams {
		_, allM[i] = beam.CalcVandM(dom.Sol, 0, nstations)
	}

	// scaling factor
	maxAbsM := la.MatLargest(allM, 1)
	dist := utl.Max(dom.Msh.Xmax-dom.Msh.Xmin, dom.Msh.Ymax-dom.Msh.Ymin)
	sf := 1.0
	if maxAbsM > 1e-7 {
		sf = coef * dist / maxAbsM
	}

	// draw
	dom.Msh.Draw2d()
	for i, beam := range beams {
		beam.PlotDiagMoment(allM[i], withtext, numfmt, tolM, sf)
	}
	return
}
コード例 #6
0
ファイル: io.go プロジェクト: yunpeng1/gosl
func GetLimits(o *Nurbs) (xmin, xmax, xdel []float64) {
	xmin = []float64{math.Inf(+1), math.Inf(+1), math.Inf(+1)}
	xmax = []float64{math.Inf(-1), math.Inf(-1), math.Inf(-1)}
	xdel = []float64{0, 0, 0}
	for k := 0; k < o.n[2]; k++ {
		for j := 0; j < o.n[1]; j++ {
			for i := 0; i < o.n[0]; i++ {
				x := o.GetQ(i, j, k)
				for r := 0; r < o.gnd; r++ {
					xmin[r] = utl.Min(xmin[r], x[r])
					xmax[r] = utl.Max(xmax[r], x[r])
				}
			}
		}
	}
	for i := 0; i < 3; i++ {
		xdel[i] = xmax[i] - xmin[i]
	}
	for i := o.gnd; i < 3; i++ {
		xmin[i] = 0
		xmax[i] = 0
		xdel[i] = 0
	}
	return
}
コード例 #7
0
ファイル: refm1.go プロジェクト: PaddySchmidt/gofem
// derivatives
func (o *RefM1) Derivs(pc, sl float64, wet bool) (L, Lx, J, Jx, Jy float64, err error) {
	if pc <= 0 {
		return
	}
	if sl < o.yr {
		sl = o.yr
	}
	x := math.Log(1.0 + pc)
	var DλbDx, DλbDy, D2λbDx2, D2λbDyDx, D2λbDy2 float64
	if wet && !o.nowet {
		o.wetting(x, sl)
		DywDx := -o.λw - o.c1w*o.c2w*math.Exp(o.c1w*x)/(o.βw*(o.c3w+o.c2w*math.Exp(o.c1w*x)))
		DλbDx = o.β1 * o.λb * DywDx
		DλbDy = (o.βw*(o.λwb-o.λw) - o.λwb*o.β1) * math.Exp(-o.β1*o.D)
		D2ywDx2 := -o.c1w * o.c1w * o.c2w * o.c3w * math.Exp(o.c1w*x) / (o.βw * math.Pow(o.c3w+o.c2w*math.Exp(o.c1w*x), 2.0))
		D2λbDx2 = o.β1 * (DλbDx*DywDx + o.λb*D2ywDx2)
		D2λbDyDx = o.β1 * DλbDy * DywDx
		DλwbDy := -o.λw * math.Exp(-o.βw*o.Dw) * o.βw
		D2λbDy2 = (o.βw*DλwbDy-DλwbDy*o.β1)*math.Exp(-o.β1*o.D) - (o.βw*(o.λwb-o.λw)-o.λwb*o.β1)*math.Exp(-o.β1*o.D)*o.β1
	} else {
		o.drying(x, sl)
		DydDx := -o.λd + o.c1d*o.c2d*math.Exp(o.c1d*x)/(o.βd*(o.c3d+o.c2d*math.Exp(o.c1d*x)))
		DλbDx = -o.β2b * o.λb * DydDx
		Dβ2bDy := o.α * o.β2 * math.Pow(utl.Max(sl, 0.0), o.α-1.0)
		DλbDy = (o.βd*(o.λd-o.λdb) + o.λdb*(o.β2b-Dβ2bDy*o.D)) * math.Exp(-o.β2b*o.D)
		D2ydDx2 := o.c1d * o.c1d * o.c2d * o.c3d * math.Exp(o.c1d*x) / (o.βd * math.Pow(o.c3d+o.c2d*math.Exp(o.c1d*x), 2.0))
		D2λbDx2 = -o.β2b * (DλbDx*DydDx + o.λb*D2ydDx2)
		D2λbDyDx = -(o.β2b*DλbDy + o.λb*Dβ2bDy) * DydDx
		DλdbDy := o.λd * math.Exp(-o.βd*o.Dd) * o.βd
		Dβ2bDy2 := o.α * o.β2 * math.Pow(utl.Max(sl, 0.0), o.α-2.0) * (o.α - 1.0)
		D2λbDy2 = (-o.βd*DλdbDy+DλdbDy*(o.β2b-Dβ2bDy*o.D)+o.λdb*(2.0*Dβ2bDy-Dβ2bDy2*o.D))*math.Exp(-o.β2b*o.D) + (o.βd*(o.λd-o.λdb)+o.λdb*(o.β2b-Dβ2bDy*o.D))*math.Exp(-o.β2b*o.D)*(-Dβ2bDy*o.D+o.β2b)
	}
	den := 1.0 + pc
	den2 := den * den
	L = (o.λb - DλbDx) / den2
	Lx = ((DλbDx-D2λbDx2)/den2 - 2.0*L) / den
	J = -DλbDy / den
	Jx = -(D2λbDyDx/den + J) / den
	Jy = -D2λbDy2 / den
	return
}
コード例 #8
0
ファイル: refm1.go プロジェクト: PaddySchmidt/gofem
// J computes J = ∂Cc/∂sl
func (o RefM1) J(pc, sl float64, wet bool) (float64, error) {
	if pc <= 0 {
		return 0, nil
	}
	if sl < o.yr {
		sl = o.yr
	}
	x := math.Log(1.0 + pc)
	var DλbDy float64
	if wet && !o.nowet {
		o.wetting(x, sl)
		DλbDy = (o.βw*(o.λwb-o.λw) - o.λwb*o.β1) * math.Exp(-o.β1*o.D)
	} else {
		o.drying(x, sl)
		Dβ2bDy := o.α * o.β2 * math.Pow(utl.Max(sl, 0.0), o.α-1.0)
		DλbDy = (o.βd*(o.λd-o.λdb) + o.λdb*(o.β2b-Dβ2bDy*o.D)) * math.Exp(-o.β2b*o.D)
	}
	return -DλbDy / (1.0 + pc), nil
}
コード例 #9
0
ファイル: fesim.go プロジェクト: cpmech/goga
// RunFEA runs FE analysis.
func RunFEA(x []float64, cpu int) (lsf, failed float64) {

	// FemData
	o := FEMDATA[cpu]

	// check for NaNs
	defer func() {
		if math.IsNaN(failed) || math.IsNaN(lsf) {
			io.PfRed("x = %+#v\n", x)
			chk.Panic("NaN: failed=%v lsf=%v\n", failed, lsf)
		}
	}()

	// adjust parameters
	for i, v := range o.Vars {
		v.Prm.Set(x[i])
	}
	for _, prm := range o.Sim.AdjDependent {
		if prm.N == "I22" {
			prm.Set(prm.S * prm.Other.V * prm.Other.V)
		}
	}
	o.Dom.RecomputeKM()

	// run
	err := o.Analysis.SolveOneStage(0, true)
	if err != nil {
		failed = 1
		return
	}

	// displacement based limit-state-function
	if o.AwdU > 0 {
		δ := o.Dom.Sol.Y[o.EqsU[0]]
		for i := 1; i < len(o.EqsU); i++ {
			δ = utl.Max(δ, o.Dom.Sol.Y[o.EqsU[i]])
		}
		lsf = o.AwdU - δ
	}
	return
}
コード例 #10
0
ファイル: s_richardson.go プロジェクト: PaddySchmidt/gofem
func (o *RichardsonExtrap) Run(tf float64, dtFunc, dtoFunc fun.Func, verbose bool, dbgKb DebugKb_t) (err error) {

	// constants
	dat := o.doms[0].Sim.Solver
	atol := dat.REatol
	rtol := dat.RErtol
	mmin := dat.REmmin
	mmax := dat.REmmax
	mfac := dat.REmfac

	// control
	t := o.doms[0].Sol.T
	tout := t + dtoFunc.F(t, nil)
	steady := o.doms[0].Sim.Data.Steady

	// first output
	if o.sum != nil {
		err = o.sum.SaveDomains(t, o.doms, false)
		if err != nil {
			return chk.Err("cannot save results:\n%v", err)
		}
	}

	// domain and variables
	d := o.doms[0]
	o.Y_big = make([]float64, d.Ny)

	// time loop
	o.Δt = dtFunc.F(t, nil)
	o.Δtcpy = o.Δt
	var ΔtOld, rerrOld float64
	for t < tf {

		// check for continued divergence
		if o.ndiverg >= dat.NdvgMax {
			return chk.Err("continuous divergence after %d steps reached", o.ndiverg)
		}

		// check time increment
		if o.Δt < dat.DtMin {
			return chk.Err("Δt increment is too small: %g < %g", o.Δt, dat.DtMin)
		}

		// dynamic coefficients
		if !steady {
			err = o.dc.CalcBoth(o.Δt)
			if err != nil {
				return chk.Err("cannot compute dynamic coefficients:\n%v", err)
			}
		}

		// check for maximum number of substeps
		o.nsteps += 1
		if o.nsteps >= dat.REnssmax {
			return chk.Err("RE: max number of steps reached: %d", o.nsteps)
		}

		// backup domain
		d.backup()

		// single step with Δt
		d.Sol.T = t + o.Δt
		d.Sol.Dt = o.Δt
		o.diverging, err = run_iterations(t+o.Δt, o.Δt, d, o.dc, o.sum, dbgKb)
		if err != nil {
			return chk.Err("single step with Δt: run_iterations failed:\n%v", err)
		}
		if dat.DvgCtrl {
			if o.divergence_control(d, "big step", verbose) {
				continue
			}
		}

		// save intermediate state
		for i := 0; i < d.Ny; i++ {
			o.Y_big[i] = d.Sol.Y[i]
		}

		// restore initial state
		d.restore()

		// 1st halved step
		d.Sol.T = t + o.Δt/2.0
		d.Sol.Dt = o.Δt / 2.0
		o.diverging, err = run_iterations(t+o.Δt/2.0, o.Δt/2.0, d, o.dc, o.sum, dbgKb)
		if err != nil {
			return chk.Err("1st halved step: run_iterations failed:\n%v", err)
		}
		if dat.DvgCtrl {
			if o.divergence_control(d, "1st half step", verbose) {
				continue
			}
		}

		// 2nd halved step
		d.Sol.T = t + o.Δt
		d.Sol.Dt = o.Δt
		o.diverging, err = run_iterations(t+o.Δt, o.Δt/2.0, d, o.dc, o.sum, dbgKb)
		if err != nil {
			return chk.Err("2nd halved step: run_iterations failed:\n%v", err)
		}
		if dat.DvgCtrl {
			if o.divergence_control(d, "2nd half step", verbose) {
				continue
			}
		}

		// Richardson's extrapolation error
		rerr := la.VecRmsError(d.Sol.Y, o.Y_big, atol, rtol, d.Sol.Y) / 3.0

		// step size change
		m := utl.Min(mmax, utl.Max(mmin, mfac*math.Pow(1.0/rerr, 1.0/2.0)))
		ΔtNew := m * o.Δt

		// accepted
		if rerr < 1.0 {

			// update variables
			o.naccept += 1
			t += o.Δt
			d.Sol.T = t

			// output
			if verbose {
				if !dat.ShowR {
					io.PfWhite("%30.15f\r", t)
				}
			}
			if t >= tout || o.laststep {
				if o.sum != nil {
					err = o.sum.SaveDomains(t, o.doms, false)
					if err != nil {
						return chk.Err("cannot save results:\n%v", err)
					}
				}
				tout += dtoFunc.F(t, nil)
			}

			// reached final time
			if o.laststep {
				if verbose {
					io.Pfgreen("\n\nRichardson extrapolation succeeded\n")
				}
				return
			}

			// predictive controller of Gustafsson
			if !dat.REnogus {
				if o.naccept > 1 {
					m = mfac * (o.Δt / ΔtOld) * math.Sqrt(1.0/rerr) * math.Sqrt(rerrOld/rerr)
					if m*o.Δt < ΔtNew {
						o.ngustaf += 1
					}
					ΔtNew = utl.Min(ΔtNew, m*o.Δt)
				}
				ΔtOld = o.Δt
				rerrOld = utl.Max(0.9, rerr) // 1e-2
			}

			// next step size
			if o.reject { // do not alow Δt to grow if previous was a reject
				ΔtNew = utl.Min(o.Δt, ΔtNew)
			}
			o.reject = false
			o.Δt = ΔtNew
			if t+ΔtNew-tf >= 0.0 {
				o.laststep = true
				o.Δt = tf - t
			}

			// rejected
		} else {

			// restore state
			d.restore()

			// set flags
			o.nreject += 1
			o.reject = true
			o.laststep = false

			// next step size
			o.Δt = ΔtNew
			if t+o.Δt > tf {
				o.Δt = tf - t
			}
		}
	}
	return
}
コード例 #11
0
ファイル: metrics.go プロジェクト: cpmech/goga
// Compute computes limits, find non-dominated Pareto fronts, and compute crowd distances
func (o *Metrics) Compute(sols []*Solution) (nfronts int) {

	// reset variables and find limits
	z := o.Fsizes
	nsol := len(sols)
	for i, sol := range sols {

		// reset values
		sol.Nwins = 0
		sol.Nlosses = 0
		sol.FrontId = 0
		sol.DistCrowd = 0
		sol.DistNeigh = INF
		z[i] = 0

		// check oors
		for j := 0; j < o.prms.Noor; j++ {
			if math.IsNaN(sol.Oor[j]) {
				chk.Panic("NaN found in out-of-range value array\n\txFlt = %v\n\txInt = %v\n\tova = %v\n\toor = %v", sol.Flt, sol.Int, sol.Ova, sol.Oor)
			}
		}

		// ovas range
		for j := 0; j < o.prms.Nova; j++ {
			x := sol.Ova[j]
			if math.IsNaN(x) {
				chk.Panic("NaN found in objective value array\n\txFlt = %v\n\txInt = %v\n\tova = %v\n\toor = %v", sol.Flt, sol.Int, sol.Ova, sol.Oor)
			}
			if i == 0 {
				o.Omin[j] = x
				o.Omax[j] = x
			} else {
				o.Omin[j] = utl.Min(o.Omin[j], x)
				o.Omax[j] = utl.Max(o.Omax[j], x)
			}
		}

		// floats range
		for j := 0; j < o.prms.Nflt; j++ {
			x := sol.Flt[j]
			if i == 0 {
				o.Fmin[j] = x
				o.Fmax[j] = x
			} else {
				o.Fmin[j] = utl.Min(o.Fmin[j], x)
				o.Fmax[j] = utl.Max(o.Fmax[j], x)
			}
		}

		// ints range
		for j := 0; j < o.prms.Nint; j++ {
			x := sol.Int[j]
			if i == 0 {
				o.Imin[j] = x
				o.Imax[j] = x
			} else {
				o.Imin[j] = utl.Imin(o.Imin[j], x)
				o.Imax[j] = utl.Imax(o.Imax[j], x)
			}
		}
	}

	// compute neighbour distance
	for i := 0; i < nsol; i++ {
		A := sols[i]
		for j := i + 1; j < nsol; j++ {
			B := sols[j]
			o.closest(A, B)
		}
	}

	// skip if single-objective problem
	if o.prms.Nova < 2 {
		return
	}

	// compute wins/losses data
	for i := 0; i < nsol; i++ {
		A := sols[i]
		for j := i + 1; j < nsol; j++ {
			B := sols[j]
			A_win, B_win := A.Compare(B)
			if A_win {
				A.WinOver[A.Nwins] = B
				A.Nwins++
				B.Nlosses++
			}
			if B_win {
				B.WinOver[B.Nwins] = A
				B.Nwins++
				A.Nlosses++
			}
		}
	}

	// first front
	for _, sol := range sols {
		if sol.Nlosses == 0 {
			o.Fronts[0][z[0]] = sol
			z[0]++
		}
	}

	// next fronts
	for r, front := range o.Fronts {
		if z[r] == 0 {
			break
		}
		s := r + 1
		nfronts++
		for i := 0; i < z[r]; i++ {
			A := front[i]
			for j := 0; j < A.Nwins; j++ {
				B := A.WinOver[j]
				B.Nlosses--
				if B.Nlosses == 0 { // B belongs to next front
					B.FrontId = s
					o.Fronts[s][z[s]] = B
					z[s]++
				}
			}
		}
	}

	// crowd distances
	for r := 0; r < nfronts; r++ {
		l, m := z[r], z[r]-1
		if l == 1 {
			o.Fronts[r][0].DistCrowd = -1
			continue
		}
		F := o.Fronts[r][:l]
		for j := 0; j < o.prms.Nova; j++ {
			SortByOva(F, j)
			δ := o.Omax[j] - o.Omin[j] + 1e-15
			F[0].DistCrowd = INF
			F[m].DistCrowd = INF
			for i := 1; i < m; i++ {
				F[i].DistCrowd += ((F[i].Ova[j] - F[i-1].Ova[j]) / δ) * ((F[i+1].Ova[j] - F[i].Ova[j]) / δ)
			}
		}
	}
	return
}
コード例 #12
0
ファイル: topology.go プロジェクト: PaddySchmidt/gofem
// 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 {
		for fidx, cftag := range cell.C.FTags {
			if cftag == ftag {

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

				// vertices on face
				flvids := cell.C.Shp.FaceLocalVerts[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] = utl.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] = utl.Min(dat.Umin[j], x[dat.Iu[j]])
				dat.Umax[j] = utl.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
}
コード例 #13
0
ファイル: geost.go プロジェクト: PaddySchmidt/gofem
// SetGeoSt sets the initial state to a hydrostatic condition
func (o *Domain) SetGeoSt(stg *inp.Stage) (err error) {

	// check layers definition
	geo := stg.GeoSt
	if len(geo.Layers) < 1 {
		return chk.Err("geost: layers must be defined by specifying what tags belong to which layer")
	}

	// get region
	if len(o.Sim.Regions) != 1 {
		return chk.Err("geost: can only handle one domain for now")
	}
	reg := o.Sim.Regions[0]

	// gravity
	grav := o.Sim.Gravity.F(0, nil)

	// fix UseK0
	nlayers := len(geo.Layers)
	if len(geo.UseK0) != nlayers {
		geo.UseK0 = make([]bool, nlayers)
	}

	// initialise layers
	var L GeoLayers
	L = make([]*GeoLayer, nlayers)
	ndim := o.Sim.Ndim
	nodehandled := make(map[int]bool)
	ctaghandled := make(map[int]bool) // required to make sure all elements were initialised
	for i, tags := range geo.Layers {

		// new layer
		L[i] = new(GeoLayer)
		L[i].Tags = tags
		L[i].Zmin = o.Sim.MaxElev
		L[i].Zmax = 0
		L[i].Cl = o.Sim.WaterRho0 / o.Sim.WaterBulk

		// get porous parameters
		L[i].RhoS0, L[i].nf0, err = get_porous_parameters(o.Sim.MatParams, reg, tags[0])
		if err != nil {
			return
		}

		// parameters
		if geo.UseK0[i] {
			L[i].K0 = geo.K0[i]
		} else {
			L[i].K0 = geo.Nu[i] / (1.0 - geo.Nu[i])
		}
		if L[i].K0 < 1e-7 {
			return chk.Err("geost: K0 or Nu is incorect: K0=%g, Nu=%g", L[i].K0, geo.Nu)
		}

		// for each tag of cells in this layer
		for _, tag := range tags {

			// check tags
			cells := o.Msh.CellTag2cells[tag]
			if len(cells) < 1 {
				return chk.Err("geost: there are no cells with tag = %d", tag)
			}

			// set nodes and elements and find min and max z-coordinates
			for _, c := range cells {
				L[i].Elems = append(L[i].Elems, o.Cid2elem[c.Id])
				for _, v := range c.Verts {
					if !nodehandled[v] {
						L[i].Nodes = append(L[i].Nodes, o.Vid2node[v])
					}
					L[i].Zmin = utl.Min(L[i].Zmin, o.Msh.Verts[v].C[ndim-1])
					L[i].Zmax = utl.Max(L[i].Zmax, o.Msh.Verts[v].C[ndim-1])
					nodehandled[v] = true
				}
				ctaghandled[c.Tag] = true
			}
		}
	}

	// make sure all elements tags were handled
	for tag, _ := range o.Msh.CellTag2cells {
		if !ctaghandled[tag] {
			return chk.Err("geost: there are cells not included in any layer: ctag=%d", tag)
		}
	}

	// sort layers from top to bottom
	sort.Sort(L)

	// set previous/top states in layers and compute Sol.Y
	for i, lay := range L {

		// previous state
		var top *geostate
		ρS := lay.RhoS0
		nf := lay.nf0
		sl := 1.0
		if i == 0 {
			pl := (o.Sim.WaterLevel - o.Sim.MaxElev) * o.Sim.WaterRho0 * grav
			ρL := o.Sim.WaterRho0
			ρ := nf*sl*ρL + (1.0-nf)*ρS
			σV := -o.Sim.Data.Surch
			top = &geostate{pl, ρL, ρ, σV}
		} else {
			top, err = L[i-1].Calc(L[i-1].Zmin)
			if err != nil {
				return chk.Err("cannot compute state @ bottom of layer:\n%v", err)
			}
			ρL := top.ρL
			top.ρ = nf*sl*ρL + (1.0-nf)*ρS
		}

		// start layer
		lay.Start(top, grav)

		// set nodes
		for _, nod := range lay.Nodes {
			z := nod.Vert.C[ndim-1]
			s, err := lay.Calc(z)
			if err != nil {
				return chk.Err("cannot compute state @ node z = %g\n%v", z, err)
			}
			dof := nod.GetDof("pl")
			if dof != nil {
				o.Sol.Y[dof.Eq] = s.pl
			}
		}

		// set elements
		for _, elem := range lay.Elems {
			if ele, okk := elem.(ElemIntvars); okk {

				// build slices
				coords := ele.Ipoints()
				nip := len(coords)
				svT := make([]float64, nip) // total vertical stresses
				for i := 0; i < nip; i++ {
					z := coords[i][ndim-1]
					s, err := lay.Calc(z)
					if err != nil {
						return chk.Err("cannot compute state @ ip z = %g\n%v", z, err)
					}
					svT[i] = -s.σV
				}
				ivs := map[string][]float64{"svT": svT, "K0": []float64{lay.K0}}

				// set element's states
				err = ele.SetIniIvs(o.Sol, ivs)
				if err != nil {
					return chk.Err("geost: element's internal values setting failed:\n%v", err)
				}
			}
		}
	}
	return
}
コード例 #14
0
ファイル: search.go プロジェクト: yunpeng1/gosl
// FindAlongSegment gets the ids of entries that lie close to a segment
//  Note: the initial (xi) and final (xf) points on segment defined a bounding box of valid points
func (o Bins) FindAlongSegment(xi, xf []float64, tol float64) []int {

	// auxiliary variables
	var sbins []*Bin // selected bins
	lmax := utl.Max(o.S[0], o.S[1])
	if o.Ndim == 3 {
		lmax = utl.Max(lmax, o.S[2])
	}
	btol := 0.9 * lmax // tolerance for bins
	var p, pi, pf Point
	pi.X = xi[0]
	pf.X = xf[0]
	pi.Y = xi[1]
	pf.Y = xf[1]
	if o.Ndim == 3 {
		pi.Z = xi[2]
		pf.Z = xf[2]
	} else {
		xi = []float64{xi[0], xi[1], -1}
		xf = []float64{xf[0], xf[1], 1}
	}

	// loop along all bins
	var i, j, k int
	var x, y, z float64
	nxy := o.N[0] * o.N[1]
	for idx, bin := range o.All {

		// skip empty bins
		if bin == nil {
			continue
		}

		// coordinates of bin center
		i = idx % o.N[0] // indices representing bin
		j = (idx % nxy) / o.N[0]
		x = o.Xi[0] + float64(i)*o.S[0] // coordinates of bin corner
		y = o.Xi[1] + float64(j)*o.S[1]
		x += o.S[0] / 2.0
		y += o.S[1] / 2.0
		if o.Ndim == 3 {
			k = idx / nxy
			z = o.Xi[2] + float64(k)*o.S[2]
			z += o.S[2] / 2.0
		}

		// check if bin is near line
		p = Point{x, y, z}
		d := DistPointLine(&p, &pi, &pf, tol, false)
		if d <= btol {
			sbins = append(sbins, bin)
		}
	}

	// entries ids
	var ids []int

	// find closest points
	for _, bin := range sbins {
		for _, entry := range bin.Entries {
			x = entry.X[0]
			y = entry.X[1]
			if o.Ndim == 3 {
				z = entry.X[0]
			}
			p := Point{x, y, z}
			d := DistPointLine(&p, &pi, &pf, tol, false)
			if d <= tol {
				if IsPointIn(&p, xi, xf, tol) {
					ids = append(ids, entry.Id)
				}
			}
		}
	}
	return ids
}
コード例 #15
0
ファイル: plotter.go プロジェクト: PaddySchmidt/gofem
func (o *Plotter) Plot_oct(x, y []float64, res []*State, sts [][]float64, last bool) {
	// stress path
	nr := len(res)
	k := nr - 1
	var σa, σb, xmi, xma, ymi, yma float64
	for i := 0; i < nr; i++ {
		σa, σb, _ = tsr.PQW2O(o.P[i], o.Q[i], o.W[i])
		x[i], y[i] = σa, σb
		o.maxR = utl.Max(o.maxR, math.Sqrt(σa*σa+σb*σb))
		if i == 0 {
			xmi, xma = x[i], x[i]
			ymi, yma = y[i], y[i]
		} else {
			xmi = utl.Min(xmi, x[i])
			xma = utl.Max(xma, x[i])
			ymi = utl.Min(ymi, y[i])
			yma = utl.Max(yma, y[i])
		}
	}
	plt.Plot(x, y, io.Sf("'r.', ls='%s', clip_on=0, color='%s', marker='%s', label=r'%s'", o.Ls, o.Clr, o.Mrk, o.Lbl))
	plt.PlotOne(x[0], y[0], io.Sf("'bo', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.SpMrk, o.SpMs))
	plt.PlotOne(x[k], y[k], io.Sf("'bs', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.EpMrk, o.EpMs))
	// fix range and max radius
	xmi, xma, ymi, yma = o.fix_range(0, xmi, xma, ymi, yma)
	rr := math.Sqrt((xma-xmi)*(xma-xmi) + (yma-ymi)*(yma-ymi))
	if o.maxR < rr {
		o.maxR = rr
	}
	if o.maxR < 1e-10 {
		o.maxR = 1
	}
	if yma > -xmi {
		xmi = -yma
	}
	if o.OctLims != nil {
		xmi, xma, ymi, yma = o.OctLims[0], o.OctLims[1], o.OctLims[2], o.OctLims[3]
	}
	//xmi, xma, ymi, yma = -20000, 20000, -20000, 20000
	// yield surface
	var σcmax float64
	if o.WithYs && o.m != nil {
		//io.Pforan("xmi,xma ymi,yma = %v,%v %v,%v\n", xmi,xma, ymi,yma)
		dx := (xma - xmi) / float64(o.NptsOct-1)
		dy := (yma - ymi) / float64(o.NptsOct-1)
		xx := la.MatAlloc(o.NptsOct, o.NptsOct)
		yy := la.MatAlloc(o.NptsOct, o.NptsOct)
		zz := la.MatAlloc(o.NptsOct, o.NptsOct)
		var λ0, λ1, λ2, σc float64
		v := NewState(len(res[0].Sig), len(res[0].Alp), false, len(res[0].EpsE) > 0)
		for k := 0; k < nr; k++ {
			copy(v.Alp, res[k].Alp)
			v.Dgam = res[k].Dgam
			σc = tsr.M_p(res[k].Sig) * tsr.SQ3
			//σc = 30000
			σcmax = utl.Max(σcmax, σc)
			for i := 0; i < o.NptsOct; i++ {
				for j := 0; j < o.NptsOct; j++ {
					xx[i][j] = xmi + float64(i)*dx
					yy[i][j] = ymi + float64(j)*dy
					λ0, λ1, λ2 = tsr.O2L(xx[i][j], yy[i][j], σc)
					v.Sig[0], v.Sig[1], v.Sig[2] = λ0, λ1, λ2
					ys := o.m.YieldFuncs(v)
					zz[i][j] = ys[0]
				}
			}
			plt.ContourSimple(xx, yy, zz, false, 8, io.Sf("colors=['%s'], levels=[0], linestyles=['%s'], linewidths=[%g], clip_on=0", o.YsClr0, o.YsLs0, o.YsLw0)+o.ArgsYs)

		}
	}
	// predictor-corrector
	if len(o.PreCor) > 1 {
		var σa, σb, σanew, σbnew float64
		for i := 1; i < len(o.PreCor); i++ {
			σa, σb, _ = tsr.M_oct(o.PreCor[i-1])
			σanew, σbnew, _ = tsr.M_oct(o.PreCor[i])
			if math.Abs(σanew-σa) > 1e-7 || math.Abs(σbnew-σb) > 1e-7 {
				//plt.Plot([]float64{σa,σanew}, []float64{σb,σbnew}, "'k+', ms=3, color='k'")
				plt.Arrow(σa, σb, σanew, σbnew, io.Sf("sc=%d, fc='%s', ec='%s'", o.ArrWid, o.ClrPC, o.ClrPC))
			}
			o.maxR = utl.Max(o.maxR, math.Sqrt(σa*σa+σb*σb))
			o.maxR = utl.Max(o.maxR, math.Sqrt(σanew*σanew+σbnew*σbnew))
		}
	}
	// rosette and settings
	if last {
		tsr.PlotRefOct(o.Phi, σcmax, true)
		tsr.PlotRosette(o.maxR, false, true, true, 6)
		if o.OctAxOff {
			plt.AxisOff()
		}
		plt.Gll("$\\sigma_a$", "$\\sigma_b$", "")
		if lims, ok := o.Lims["oct"]; ok {
			plt.AxisLims(lims)
		}
		if lims, ok := o.Lims["oct,ys"]; ok {
			plt.AxisLims(lims)
		}
	}
}
コード例 #16
0
ファイル: plotter.go プロジェクト: PaddySchmidt/gofem
func (o *Plotter) Plot_p_q(x, y []float64, res []*State, sts [][]float64, last bool) {
	// stress path
	nr := len(res)
	k := nr - 1
	var xmi, xma, ymi, yma float64
	for i := 0; i < nr; i++ {
		x[i], y[i] = o.P[i], o.Q[i]
		if o.Multq {
			mult := fun.Sign(o.W[i])
			y[i] *= mult
		}
		if o.UseOct {
			x[i] *= tsr.SQ3
			y[i] *= tsr.SQ2by3
		}
		if i == 0 {
			xmi, xma = x[i], x[i]
			ymi, yma = y[i], y[i]
		} else {
			xmi = utl.Min(xmi, x[i])
			xma = utl.Max(xma, x[i])
			ymi = utl.Min(ymi, y[i])
			yma = utl.Max(yma, y[i])
		}
		if o.SMPon {
			x[i], y[i], _ = tsr.M_pq_smp(res[i].Sig, o.SMPa, o.SMPb, o.SMPβ, o.SMPϵ)
		}
	}
	plt.Plot(x, y, io.Sf("'r.', ls='%s', clip_on=0, color='%s', marker='%s', label=r'%s'", o.Ls, o.Clr, o.Mrk, o.Lbl))
	plt.PlotOne(x[0], y[0], io.Sf("'bo', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.SpMrk, o.SpMs))
	plt.PlotOne(x[k], y[k], io.Sf("'bs', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.EpMrk, o.EpMs))
	// yield surface
	if o.WithYs && o.m != nil {
		mx, my := 1.0, 1.0
		if o.UseOct {
			mx, my = tsr.SQ3, tsr.SQ2by3
		}
		if o.UsePmin {
			xmi = utl.Min(xmi, o.Pmin*mx)
		}
		if o.UsePmax {
			xma = utl.Max(xma, o.Pmax*mx)
			yma = utl.Max(yma, o.Pmax*my)
		}
		xmi, xma, ymi, yma = o.fix_range(xmi, xmi, xma, ymi, yma)
		if o.PqLims != nil {
			xmi, xma, ymi, yma = o.PqLims[0], o.PqLims[1], o.PqLims[2], o.PqLims[3]
		}
		//io.Pforan("xmi,xma ymi,yma = %v,%v %v,%v\n", xmi,xma, ymi,yma)
		dx := (xma - xmi) / float64(o.NptsPq-1)
		dy := (yma - ymi) / float64(o.NptsPq-1)
		xx := la.MatAlloc(o.NptsPq, o.NptsPq)
		yy := la.MatAlloc(o.NptsPq, o.NptsPq)
		za := la.MatAlloc(o.NptsPq, o.NptsPq)
		zb := la.MatAlloc(o.NptsPq, o.NptsPq)
		var p, q, σa, σb, σc, λ0, λ1, λ2 float64
		v := NewState(len(res[0].Sig), len(res[0].Alp), false, len(res[0].EpsE) > 0)
		for k := 0; k < nr; k++ {
			copy(v.Alp, res[k].Alp)
			v.Dgam = res[k].Dgam
			for i := 0; i < o.NptsPq; i++ {
				for j := 0; j < o.NptsPq; j++ {
					xx[i][j] = xmi + float64(i)*dx
					yy[i][j] = ymi + float64(j)*dy
					p, q = xx[i][j], yy[i][j]
					if o.UseOct {
						p /= tsr.SQ3
						q /= tsr.SQ2by3
					}
					σa, σb, σc = tsr.PQW2O(p, q, o.W[k])
					λ0, λ1, λ2 = tsr.O2L(σa, σb, σc)
					v.Sig[0], v.Sig[1], v.Sig[2] = λ0, λ1, λ2
					ys := o.m.YieldFuncs(v)
					za[i][j] = ys[0]
					if o.nsurf > 1 {
						zb[i][j] = ys[1]
					}
					if o.SMPon {
						xx[i][j], yy[i][j], _ = tsr.M_pq_smp(v.Sig, o.SMPa, o.SMPb, o.SMPβ, o.SMPϵ)
					}
				}
			}
			plt.ContourSimple(xx, yy, za, false, 8, io.Sf("colors=['%s'], levels=[0], linestyles=['%s'], linewidths=[%g], clip_on=0", o.YsClr0, o.YsLs0, o.YsLw0)+o.ArgsYs)
			if o.nsurf > 1 {
				plt.ContourSimple(xx, yy, zb, false, 8, io.Sf("colors=['%s'], levels=[0], linestyles=['%s'], linewidths=[%g], clip_on=0", o.YsClr1, o.YsLs1, o.YsLw1)+o.ArgsYs)
			}
		}
	}
	// predictor-corrector
	if len(o.PreCor) > 1 {
		var p, q, pnew, qnew float64
		for i := 1; i < len(o.PreCor); i++ {
			p = tsr.M_p(o.PreCor[i-1])
			q = tsr.M_q(o.PreCor[i-1])
			pnew = tsr.M_p(o.PreCor[i])
			qnew = tsr.M_q(o.PreCor[i])
			if o.UseOct {
				p *= tsr.SQ3
				pnew *= tsr.SQ3
				q *= tsr.SQ2by3
				qnew *= tsr.SQ2by3
			}
			if o.SMPon {
				p, q, _ = tsr.M_pq_smp(o.PreCor[i-1], o.SMPa, o.SMPb, o.SMPβ, o.SMPϵ)
				pnew, qnew, _ = tsr.M_pq_smp(o.PreCor[i], o.SMPa, o.SMPb, o.SMPβ, o.SMPϵ)
			}
			if math.Abs(pnew-p) > 1e-10 || math.Abs(qnew-q) > 1e-10 {
				plt.Arrow(p, q, pnew, qnew, io.Sf("sc=%d, fc='%s', ec='%s'", o.ArrWid, o.ClrPC, o.ClrPC))
			}
		}
	}
	// settings
	if last {
		plt.Equal()
		xl, yl := "$p_{cam}$", "$q_{cam}$"
		if o.UseOct {
			xl, yl = "$p_{oct}$", "$q_{oct}$"
		}
		if o.SMPon {
			xl, yl = "$p_{smp}$", "$q_{smp}$"
		}
		if o.AxLblX != "" {
			xl = o.AxLblX
		}
		if o.AxLblY != "" {
			yl = o.AxLblY
		}
		plt.Gll(xl, yl, "leg_out=1, leg_ncol=4, leg_hlen=1.5")
		if lims, ok := o.Lims["p,q"]; ok {
			plt.AxisLims(lims)
		}
		if lims, ok := o.Lims["p,q,ys"]; ok {
			plt.AxisLims(lims)
		}
	}
}
コード例 #17
0
ファイル: plotter.go プロジェクト: PaddySchmidt/gofem
func (o *Plotter) Plot_s3_s1(x, y []float64, res []*State, sts [][]float64, last bool) {
	// stress path
	nr := len(res)
	k := nr - 1
	x2 := make([]float64, nr)
	var xmi, xma, ymi, yma float64
	for i := 0; i < nr; i++ {
		σ1, σ2, σ3, err := tsr.M_PrincValsNum(res[i].Sig)
		if err != nil {
			chk.Panic("computation of eigenvalues failed", err)
		}
		x[i], y[i] = -tsr.SQ2*σ3, -σ1
		x2[i] = -tsr.SQ2 * σ2
		if i == 0 {
			xmi, xma = x[i], x[i]
			ymi, yma = y[i], y[i]
		} else {
			xmi = utl.Min(utl.Min(xmi, x[i]), x2[i])
			xma = utl.Max(utl.Max(xma, x[i]), x2[i])
			ymi = utl.Min(ymi, y[i])
			yma = utl.Max(yma, y[i])
		}
	}
	plt.Plot(x, y, io.Sf("'r.', ls='%s', clip_on=0, color='%s', marker='%s', label=r'$\\sigma_3$ %s'", o.Ls, o.Clr, o.Mrk, o.Lbl))
	plt.Plot(x2, y, io.Sf("'r.', ls='%s', clip_on=0, color='%s', marker='%s', label=r'$\\sigma_2$ %s'", o.LsAlt, o.Clr, o.Mrk, o.Lbl))
	plt.PlotOne(x[0], y[0], io.Sf("'bo', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.SpMrk, o.SpMs))
	plt.PlotOne(x[k], y[k], io.Sf("'bs', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.EpMrk, o.EpMs))
	// yield surface
	if o.WithYs && o.m != nil {
		if o.UsePmin {
			xmi = utl.Min(xmi, o.Pmin*tsr.SQ2)
			ymi = utl.Min(ymi, o.Pmin)
		}
		if o.UsePmax {
			xma = utl.Max(xma, o.Pmax*tsr.SQ2)
			yma = utl.Max(yma, o.Pmax)
		}
		xmi, xma, ymi, yma = o.fix_range(0, xmi, xma, ymi, yma)
		if o.S3s1Lims != nil {
			xmi, xma, ymi, yma = o.S3s1Lims[0], o.S3s1Lims[1], o.S3s1Lims[2], o.S3s1Lims[3]
		}
		//io.Pforan("xmi,xma ymi,yma = %v,%v %v,%v\n", xmi,xma, ymi,yma)
		dx := (xma - xmi) / float64(o.NptsSig-1)
		dy := (yma - ymi) / float64(o.NptsSig-1)
		xx := la.MatAlloc(o.NptsSig, o.NptsSig)
		yy := la.MatAlloc(o.NptsSig, o.NptsSig)
		zz := la.MatAlloc(o.NptsSig, o.NptsSig)
		v := NewState(len(res[0].Sig), len(res[0].Alp), false, len(res[0].EpsE) > 0)
		for k := 0; k < nr; k++ {
			copy(v.Alp, res[k].Alp)
			v.Dgam = res[k].Dgam
			for i := 0; i < o.NptsSig; i++ {
				for j := 0; j < o.NptsSig; j++ {
					xx[i][j] = xmi + float64(i)*dx
					yy[i][j] = ymi + float64(j)*dy
					v.Sig[0], v.Sig[1], v.Sig[2] = -yy[i][j], -xx[i][j]/tsr.SQ2, -xx[i][j]/tsr.SQ2
					ys := o.m.YieldFuncs(v)
					zz[i][j] = ys[0]
				}
			}
			plt.ContourSimple(xx, yy, zz, false, 8, io.Sf("colors=['%s'], levels=[0], linestyles=['%s'], linewidths=[%g], clip_on=0", o.YsClr0, o.YsLs0, o.YsLw0)+o.ArgsYs)
		}
	}
	// predictor-corrector
	if len(o.PreCor) > 1 {
		var σ3, σ1, σ3new, σ1new float64
		for i := 1; i < len(o.PreCor); i++ {
			σ1, _, σ3, _ = tsr.M_PrincValsNum(o.PreCor[i-1])
			σ1new, _, σ3new, _ = tsr.M_PrincValsNum(o.PreCor[i])
			if math.Abs(σ3new-σ3) > 1e-7 || math.Abs(σ1new-σ1) > 1e-7 {
				plt.Arrow(-σ3*tsr.SQ2, -σ1, -σ3new*tsr.SQ2, -σ1new, io.Sf("sc=%d, fc='%s', ec='%s'", o.ArrWid, o.ClrPC, o.ClrPC))
			}
		}
	}
	// settings
	if last {
		plt.Equal()
		plt.Gll("$-\\sqrt{2}\\sigma_3$", "$-\\sigma_1$", "leg=1, leg_out=1, leg_ncol=4, leg_hlen=2")
		if lims, ok := o.Lims["s3,s1"]; ok {
			plt.AxisLims(lims)
		}
		if lims, ok := o.Lims["s3,s1,ys"]; ok {
			plt.AxisLims(lims)
		}
	}
}
コード例 #18
0
ファイル: msh.go プロジェクト: PaddySchmidt/gofem
// 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
}
コード例 #19
0
ファイル: t_floats_test.go プロジェクト: postfix/goga-1
func Test_flt03(tst *testing.T) {

	//verbose()
	chk.PrintTitle("flt03. sin⁶(5 π x) multimodal")

	// configuration
	C := NewConfParams()
	C.Nova = 1
	C.Noor = 2
	C.Nisl = 4
	C.Ninds = 24
	C.GAtype = "crowd"
	C.DiffEvol = true
	C.CrowdSize = 3
	C.ParetoPhi = 0.01
	C.CompProb = true
	C.Tf = 100
	C.Dtmig = 60
	C.RangeFlt = [][]float64{{0, 0.9999999999999}}
	C.PopFltGen = PopFltGen
	C.CalcDerived()
	rnd.Init(C.Seed)

	// post-processing function
	values := utl.Deep3alloc(C.Tf/10, C.Nisl, C.Ninds)
	C.PostProc = func(idIsland, time int, pop Population) {
		if time%10 == 0 {
			k := time / 10
			for i, ind := range pop {
				values[k][idIsland][i] = ind.GetFloat(0)
			}
		}
	}

	// functions
	yfcn := func(x float64) float64 { return math.Pow(math.Sin(5.0*math.Pi*x), 6.0) }
	fcn := func(f, g, h []float64, x []float64) {
		f[0] = -yfcn(x[0])
	}

	// simple problem
	sim := NewSimpleFltProb(fcn, 1, 0, 0, C)
	sim.Run(chk.Verbose)

	// write histograms and plot
	if chk.Verbose {

		// write histograms
		var buf bytes.Buffer
		hist := rnd.Histogram{Stations: utl.LinSpace(0, 1, 13)}
		for k := 0; k < C.Tf/10; k++ {
			for i := 0; i < C.Nisl; i++ {
				clear := false
				if i == 0 {
					clear = true
				}
				hist.Count(values[k][i], clear)
			}
			io.Ff(&buf, "\ntime=%d\n%v", k*10, rnd.TextHist(hist.GenLabels("%4.2f"), hist.Counts, 60))
		}
		io.WriteFileVD("/tmp/goga", "test_flt03_hist.txt", &buf)

		// plot
		plt.SetForEps(0.8, 300)
		xmin := sim.Evo.Islands[0].Pop[0].GetFloat(0)
		xmax := xmin
		for k := 0; k < C.Nisl; k++ {
			for _, ind := range sim.Evo.Islands[k].Pop {
				x := ind.GetFloat(0)
				y := yfcn(x)
				xmin = utl.Min(xmin, x)
				xmax = utl.Max(xmax, x)
				plt.PlotOne(x, y, "'r.',clip_on=0,zorder=20")
			}
		}
		np := 401
		X := utl.LinSpace(0, 1, np)
		Y := make([]float64, np)
		for i := 0; i < np; i++ {
			Y[i] = yfcn(X[i])
		}
		plt.Plot(X, Y, "'b-',clip_on=0,zorder=10")
		plt.Gll("$x$", "$y$", "")
		plt.SaveD("/tmp/goga", "test_flt03_func.eps")
	}
}
コード例 #20
0
ファイル: out.go プロジェクト: PaddySchmidt/gofem
// Start starts handling of results given a simulation input file
func Start(simfnpath string, stageIdx, regionIdx int) {

	// fem structure
	Analysis = fem.NewFEM(simfnpath, "", false, false, true, false, false, 0)
	Dom = Analysis.Domains[regionIdx]
	Sum = Analysis.Summary

	// set stage
	err := Analysis.SetStage(stageIdx)
	if err != nil {
		chk.Panic("cannot set stage:\n%v", err)
	}

	// initialise solution vectors
	err = Analysis.ZeroStage(stageIdx, true)
	if err != nil {
		chk.Panic("cannot initialise solution vectors:\n%v", err)
	}

	// clear previous data
	Ipoints = make([]*fem.OutIpData, 0)
	Cid2ips = make([][]int, len(Dom.Msh.Cells))
	Ipkey2ips = make(map[string][]int)
	Ipkeys = make(map[string]bool)
	Planes = make(map[string]*PlaneData)
	Results = make(map[string]Points)
	TimeInds = make([]int, 0)
	Times = make([]float64, 0)
	Splots = make([]*SplotDat, 0)

	// bins
	m := Dom.Msh
	δ := TolC * 2
	xi := []float64{m.Xmin - δ, m.Ymin - δ}
	xf := []float64{m.Xmax + δ, m.Ymax + δ}
	if m.Ndim == 3 {
		xi = append(xi, m.Zmin-δ)
		xf = append(xf, m.Zmax+δ)
	}
	err = NodBins.Init(xi, xf, Ndiv)
	if err != nil {
		chk.Panic("cannot initialise bins for nodes: %v", err)
	}
	err = IpsBins.Init(xi, xf, Ndiv)
	if err != nil {
		chk.Panic("cannot initialise bins for integration points: %v", err)
	}

	// add nodes to bins
	for _, nod := range Dom.Nodes {
		err := NodBins.Append(nod.Vert.C, nod.Vert.Id)
		if err != nil {
			return
		}
	}

	// to find limits
	ndim := Dom.Msh.Ndim
	IpsMin = make([]float64, ndim)
	IpsMax = make([]float64, ndim)
	first := true

	// add integration points to slice of ips and to bins
	for cid, ele := range Dom.Cid2elem {
		if ele == nil {
			continue
		}
		dat := ele.OutIpsData()
		nip := len(dat)
		ids := make([]int, nip)
		for i, d := range dat {

			// add to bins
			ipid := len(Ipoints)
			ids[i] = ipid
			Ipoints = append(Ipoints, d)
			err = IpsBins.Append(d.X, ipid)
			if err != nil {
				chk.Panic("cannot append to bins of integration points: %v", err)
			}

			// set auxiliary map
			vals := d.Calc(Dom.Sol)
			for key, _ := range vals {
				utl.StrIntsMapAppend(&Ipkey2ips, key, ipid)
				Ipkeys[key] = true
			}

			// limits
			if first {
				for j := 0; j < ndim; j++ {
					IpsMin[j] = d.X[j]
					IpsMax[j] = d.X[j]
				}
				first = false
			} else {
				for j := 0; j < ndim; j++ {
					IpsMin[j] = utl.Min(IpsMin[j], d.X[j])
					IpsMax[j] = utl.Max(IpsMax[j], d.X[j])
				}
			}
		}
		Cid2ips[cid] = ids
	}
}
コード例 #21
0
ファイル: femsim.go プロジェクト: cpmech/goga
// RunFEM runs FE analysis.
func (o *FemData) RunFEM(Enabled []int, Areas []float64, draw int, debug bool) (mobility, failed, weight, umax, smax, errU, errS float64) {

	// check for NaNs
	defer func() {
		if math.IsNaN(mobility) || math.IsNaN(failed) || math.IsNaN(weight) || math.IsNaN(umax) || math.IsNaN(smax) || math.IsNaN(errU) || math.IsNaN(errS) {
			io.PfRed("enabled := %+#v\n", Enabled)
			io.PfRed("areas := %+#v\n", Areas)
			chk.Panic("NaN: mobility=%v failed=%v weight=%v umax=%v smax=%v errU=%v errS=%v\n", mobility, failed, weight, umax, smax, errU, errS)
		}
	}()

	// set connectivity
	if o.Opt.BinInt {
		for cid, ena := range Enabled {
			o.Dom.Msh.Cells[cid].Disabled = true
			if ena == 1 {
				o.Dom.Msh.Cells[cid].Disabled = false
			}
		}
	} else {
		for _, cell := range o.Dom.Msh.Cells {
			cid := cell.Id
			xid := o.Cid2xid[cid]
			o.Dom.Msh.Cells[cid].Disabled = true
			if Areas[xid] >= o.Opt.aEps {
				o.Dom.Msh.Cells[cid].Disabled = false
			}
		}
	}

	// set stage
	o.Analysis.SetStage(0)

	// check for required vertices
	nnod := len(o.Dom.Nodes)
	for _, vid := range o.ReqVids {
		if o.Dom.Vid2node[vid] == nil {
			//io.Pforan("required vertex (%d) missing\n", vid)
			mobility, failed, errU, errS = float64(1+2*nnod), 1, 1, 1
			return
		}
	}

	// compute mobility
	if o.Opt.Mobility {
		m := len(o.Dom.Elems)
		d := len(o.Dom.EssenBcs.Bcs)
		M := 2*nnod - m - d
		if M > 0 {
			//io.Pforan("full mobility: M=%v\n", M)
			mobility, failed, errU, errS = float64(M), 1, 1, 1
			return
		}
	}

	// set elements' cross-sectional areas and compute weight
	for _, elem := range o.Dom.Elems {
		ele := elem.(*fem.ElastRod)
		cid := ele.Cell.Id
		xid := o.Cid2xid[cid]
		ele.Mdl.A = Areas[xid]
		ele.Recompute(false)
		weight += ele.Mdl.Rho * ele.Mdl.A * ele.L
	}

	// run FE analysis
	err := o.Analysis.SolveOneStage(0, true)
	if err != nil {
		//io.Pforan("analysis failed\n")
		mobility, failed, errU, errS = 0, 1, 1, 1
		return
	}

	// find maximum deflection
	// Note that sometimes a mechanism can happen that makes the tip to move upwards
	if o.VidU >= 0 {
		eq := o.Dom.Vid2node[o.VidU].GetEq("uy")
		uy := o.Dom.Sol.Y[eq]
		umax = math.Abs(uy)
	} else {
		for _, nod := range o.Dom.Nodes {
			eq := nod.GetEq("uy")
			uy := o.Dom.Sol.Y[eq]
			umax = utl.Max(umax, math.Abs(uy))
		}
	}
	errU = fun.Ramp(umax - o.Opt.Uawd)

	// find max stress
	for _, elem := range o.Dom.Elems {
		ele := elem.(*fem.ElastRod)
		tag := ele.Cell.Tag
		sig := ele.CalcSig(o.Dom.Sol)
		smax = utl.Max(smax, math.Abs(sig))
		errS = utl.Max(errS, o.Opt.CalcErrSig(tag, sig))
	}
	errS = fun.Ramp(errS)

	// draw
	if draw > 0 {
		lwds := make(map[int]float64)
		for _, elem := range o.Dom.Elems {
			ele := elem.(*fem.ElastRod)
			cid := ele.Cell.Id
			lwds[cid] = 0.1 + ele.Mdl.A/20.0
		}
		o.Dom.Msh.Draw2d(true, false, lwds, 1)
		//plt.Title(io.Sf("weight=%.3f deflection=%.6f", weight, umax), "")
		//plt.SaveD("/tmp/goga", io.Sf("mesh-topology-%03d.eps", draw))
	}

	// debug
	if false && (umax > 0 && errS < 1e-10) {
		io.PfYel("enabled := %+#v\n", Enabled)
		io.Pf("areas := %+#v\n", Areas)
		io.Pf("weight  = %v\n", weight)
		io.Pf("umax    = %v\n", umax)
		io.Pf("smax    = %v\n", smax)
		io.Pf("errU    = %v\n", errU)
		io.Pf("errS    = %v\n", errS)

		// post-processing
		msh := o.Dom.Msh
		vid := msh.VertTag2verts[-4][0].Id
		nod := o.Dom.Vid2node[vid]
		eqy := nod.GetEq("uy")
		uy := o.Dom.Sol.Y[eqy]
		io.Pfblue2("%2d : uy = %g\n", vid, uy)
	}
	return
}
コード例 #22
0
ファイル: graph.go プロジェクト: yunpeng1/gosl
// Draw draws grid
//  r   -- radius of circles
//  W   -- width of paths
//  dwt -- δwt for positioning text (w = W/2)
//  arrow_scale -- scale for arrows. use 0 for default value
func (o *Graph) Draw(dirout, fname string, r, W, dwt, arrow_scale float64,
	verts_lbls map[int]string, verts_fsz float64, verts_clr string,
	edges_lbls map[int]string, edges_fsz float64, edges_clr string) {
	if len(o.Verts) < 1 {
		return
	}
	xmin, ymin := o.Verts[0][0], o.Verts[0][1]
	xmax, ymax := xmin, ymin
	var lbl string
	for i, v := range o.Verts {
		if verts_lbls == nil {
			lbl = io.Sf("%d", i)
		} else {
			lbl = verts_lbls[i]
		}
		plt.Text(v[0], v[1], lbl, io.Sf("clip_on=0, color='%s', fontsize=%g, ha='center', va='center'", verts_clr, verts_fsz))
		plt.Circle(v[0], v[1], r, "clip_on=0, ec='k', lw=0.8")
		xmin, ymin = utl.Min(xmin, v[0]), utl.Min(ymin, v[1])
		xmax, ymax = utl.Max(xmax, v[0]), utl.Max(ymax, v[1])
	}
	if W > 2*r {
		W = 1.8 * r
	}
	w := W / 2.0
	xa, xb := make([]float64, 2), make([]float64, 2)
	xc, dx := make([]float64, 2), make([]float64, 2)
	mu, nu := make([]float64, 2), make([]float64, 2)
	xi, xj := make([]float64, 2), make([]float64, 2)
	l := math.Sqrt(r*r - w*w)
	var L float64
	if arrow_scale <= 0 {
		arrow_scale = 20
	}
	for k, e := range o.Edges {
		L = 0.0
		for i := 0; i < 2; i++ {
			xa[i] = o.Verts[e[0]][i]
			xb[i] = o.Verts[e[1]][i]
			xc[i] = (xa[i] + xb[i]) / 2.0
			dx[i] = xb[i] - xa[i]
			L += dx[i] * dx[i]
		}
		L = math.Sqrt(L)
		mu[0], mu[1] = dx[0]/L, dx[1]/L
		nu[0], nu[1] = -dx[1]/L, dx[0]/L
		for i := 0; i < 2; i++ {
			xi[i] = xa[i] + l*mu[i] - w*nu[i]
			xj[i] = xb[i] - l*mu[i] - w*nu[i]
			xc[i] = (xi[i]+xj[i])/2.0 - dwt*nu[i]
		}
		plt.Arrow(xi[0], xi[1], xj[0], xj[1], io.Sf("st='->', sc=%g", arrow_scale))
		if edges_lbls == nil {
			lbl = io.Sf("%d", k)
		} else {
			lbl = edges_lbls[k]
		}
		plt.Text(xc[0], xc[1], lbl, io.Sf("clip_on=0, color='%s', fontsize=%g, ha='center', va='center'", edges_clr, edges_fsz))
	}
	xmin -= r
	xmax += r
	ymin -= r
	ymax += r
	plt.AxisOff()
	plt.Equal()
	plt.AxisRange(xmin, xmax, ymin, ymax)
	plt.SaveD(dirout, fname)
}
コード例 #23
0
ファイル: plotter.go プロジェクト: PaddySchmidt/gofem
func (o *Plotter) Plot_p_ev(x, y []float64, res []*State, sts [][]float64, last bool) {
	nr := len(res)
	if len(sts) != nr {
		return
	}
	k := nr - 1
	var x0, x1 []float64
	if !o.NoAlp {
		x0, x1 = make([]float64, nr), make([]float64, nr)
	}
	withα := false
	if o.LogP {
		xmin := o.calc_x(o.P[0])
		xmax := xmin
		for i := 0; i < nr; i++ {
			x[i], y[i] = o.calc_x(o.P[i]), o.Ev[i]*100.0
			if !o.NoAlp && len(res[i].Alp) > 0 {
				withα = true
				x0[i] = o.calc_x(res[i].Alp[0])
				if o.nsurf > 1 {
					x1[i] = o.calc_x(res[i].Alp[1])
				}
			}
			xmin = utl.Min(xmin, x[i])
			xmax = utl.Max(xmax, x[i])
		}
	} else {
		xmin := o.P[0]
		xmax := xmin
		for i := 0; i < nr; i++ {
			x[i], y[i] = o.P[i], o.Ev[i]*100.0
			if !o.NoAlp && len(res[i].Alp) > 0 {
				withα = true
				x0[i] = res[i].Alp[0]
				if o.nsurf > 1 {
					x1[i] = res[i].Alp[1]
				}
			}
			xmin = utl.Min(xmin, x[i])
			xmax = utl.Max(xmax, x[i])
		}
	}
	if withα {
		plt.Plot(x0, y, io.Sf("'r-', ls='--', lw=3, clip_on=0, color='grey', label=r'%s'", o.Lbl+" $\\alpha_0$"))
		if o.nsurf > 1 {
			plt.Plot(x1, y, io.Sf("'r-', ls=':', lw=3, clip_on=0, color='grey', label=r'%s'", o.Lbl+" $\\alpha_1$"))
		}
	}
	plt.Plot(x, y, io.Sf("'r.', ls='-', clip_on=0, color='%s', marker='%s', label=r'%s'", o.Clr, o.Mrk, o.Lbl))
	plt.PlotOne(x[0], y[0], io.Sf("'bo', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.SpMrk, o.SpMs))
	plt.PlotOne(x[k], y[k], io.Sf("'bs', clip_on=0, color='%s', marker='%s', ms=%d", o.SpClr, o.EpMrk, o.EpMs))
	if last {
		xlbl := "$p$"
		if o.LogP {
			xlbl = "$\\log{[1+(p+p_t)/p_r]}$"
		}
		plt.Gll(xlbl, "$\\varepsilon_v\\;[\\%]$", "leg_out=1, leg_ncol=4, leg_hlen=2")
		if lims, ok := o.Lims["p,ev"]; ok {
			plt.AxisLims(lims)
		}
	}
}