// add_natbcs_to_rhs adds natural boundary conditions to rhs func (o ElemP) add_natbcs_to_rhs(fb []float64, sol *Solution) (ok bool) { // compute surface integral var tmp float64 var ρl, pl, fl, plmax, g, rmp, rx, rf float64 for idx, nbc := range o.NatBcs { // tmp := plmax shift or qlb tmp = nbc.Fcn.F(sol.T, nil) // loop over ips of face for jdx, ipf := range o.IpsFace { // interpolation functions and gradients @ face iface := nbc.IdxFace if LogErr(o.Shp.CalcAtFaceIp(o.X, ipf, iface), "add_natbcs_to_rhs") { return } Sf := o.Shp.Sf Jf := la.VecNorm(o.Shp.Fnvec) coef := ipf.W * Jf // select natural boundary condition type switch nbc.Key { case "ql": // flux prescribed ρl = 0 for i, m := range o.Shp.FaceLocalV[iface] { ρl += Sf[i] * o.ρl_ex[m] } for i, m := range o.Shp.FaceLocalV[iface] { fb[o.Pmap[m]] -= coef * ρl * tmp * Sf[i] } case "seep": // variables extrapolated to face ρl, pl, fl = o.fipvars(iface, sol) plmax = o.Plmax[idx][jdx] - tmp if plmax < 0 { plmax = 0 } // compute residuals g = pl - plmax // Eq. (24) rmp = o.ramp(fl + o.κ*g) rx = ρl * rmp // Eq. (30) rf = fl - rmp // Eq. (26) for i, m := range o.Shp.FaceLocalV[iface] { μ := o.Vid2seepId[m] fb[o.Pmap[m]] -= coef * Sf[i] * rx fb[o.Fmap[μ]] -= coef * Sf[i] * rf } } } } return true }
// add_natbcs_to_jac adds contribution from natural boundary conditions to Jacobian func (o ElemUP) add_natbcs_to_jac(sol *Solution) (ok bool) { // compute surface integral ndim := Global.Ndim u_nverts := o.U.Shp.Nverts var shift float64 var pl, fl, plmax, g, rmp float64 for idx, nbc := range o.P.NatBcs { // plmax shift shift = nbc.Fcn.F(sol.T, nil) // loop over ips of face for jdx, ipf := range o.P.IpsFace { // interpolation functions and gradients @ face iface := nbc.IdxFace if LogErr(o.P.Shp.CalcAtFaceIp(o.P.X, ipf, iface), "up: add_natbcs_to_jac") { return } Sf := o.P.Shp.Sf Jf := la.VecNorm(o.P.Shp.Fnvec) coef := ipf.W * Jf // select natural boundary condition type switch nbc.Key { case "seep": // variables extrapolated to face _, pl, fl = o.P.fipvars(iface, sol) plmax = o.P.Plmax[idx][jdx] - shift if plmax < 0 { plmax = 0 } // compute derivatives g = pl - plmax // Eq. (24) rmp = o.P.ramp(fl + o.P.κ*g) for i, m := range o.P.Shp.FaceLocalV[iface] { for n := 0; n < u_nverts; n++ { for j := 0; j < ndim; j++ { c := j + n*ndim for l, r := range o.P.Shp.FaceLocalV[iface] { o.Kpu[m][c] += coef * Sf[i] * Sf[l] * o.dρldus_ex[r][c] * rmp } } } } } } } return true }
// contact_add_to_rhs adds contribution to rhs due to contact modelling func (o *ElemU) contact_add_to_rhs(fb []float64, sol *Solution) (err error) { // compute surface integral var qb, db, rmp, rx, rq float64 for _, nbc := range o.NatBcs { // 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 { // contact case "contact": // variables extrapolated to face qb = o.fipvars(iface, sol) xf := o.Cell.Shp.FaceIpRealCoords(o.X, ipf, iface) la.VecAdd(xf, 1, o.us) // add displacement: x = X + u db = o.contact_g(xf) // compute residuals coef := ipf[3] * o.Thickness Jf := la.VecNorm(nvec) rmp = o.contact_ramp(qb + o.κ*db) rx = rmp rq = qb - rmp for j, m := range o.Cell.Shp.FaceLocalVerts[iface] { μ := o.Vid2contactId[m] fb[o.Qmap[μ]] -= coef * Sf[j] * rq * Jf // -residual for i := 0; i < o.Ndim; i++ { r := o.Umap[i+m*o.Ndim] fb[r] -= coef * Sf[j] * rx * nvec[i] // -extra term } } } } } return }
// CalcAtIp calculates volume data such as S and G at natural coordinate r // Input: // x[ndim][nverts+?] -- coordinates matrix of solid element // ip -- integration point // Output: // S, DSdR, DxdR, DRdx, G, and J func (o *Shape) CalcAtIp(x [][]float64, ip *Ipoint, derivs bool) (err error) { // S and dSdR o.Func(o.S, o.dSdR, ip.R, ip.S, ip.T, derivs) if !derivs { return } if o.Gndim == 1 { // calculate Jvec3d == dxdR for i := 0; i < len(x); i++ { o.Jvec3d[i] = 0.0 for m := 0; m < o.Nverts; m++ { o.Jvec3d[i] += x[i][m] * o.dSdR[m][0] // dxdR := x * dSdR } } // calculate J = norm of Jvec3d o.J = la.VecNorm(o.Jvec3d) // calculate G for m := 0; m < o.Nverts; m++ { o.Gvec[m] = o.dSdR[m][0] / o.J } return } // dxdR := sum_n x * dSdR => dx_i/dR_j := sum_n x^n_i * dS^n/dR_j for i := 0; i < len(x); i++ { for j := 0; j < o.Gndim; j++ { o.dxdR[i][j] = 0.0 for n := 0; n < o.Nverts; n++ { o.dxdR[i][j] += x[i][n] * o.dSdR[n][j] } } } // dRdx := inv(dxdR) o.J, err = la.MatInv(o.dRdx, o.dxdR, MINDET) if err != nil { return } // G == dSdx := dSdR * dRdx => dS^m/dR_i := sum_i dS^m/dR_i * dR_i/dx_j la.MatMul(o.G, 1, o.dSdR, o.dRdx) return }
func Test_geninvs01(tst *testing.T) { //verbose() chk.PrintTitle("geninvs01") // coefficients for smp invariants smp_a := -1.0 smp_b := 0.5 smp_β := 1e-1 // derivative values become too high with smp_ϵ := 1e-1 // small β and ϵ @ zero // constants for checking derivatives dver := chk.Verbose dtol := 1e-6 dtol2 := 1e-6 // run tests nd := test_nd for idxA := 0; idxA < len(test_nd); idxA++ { //for idxA := 10; idxA < 11; idxA++ { // tensor and eigenvalues A := test_AA[idxA] a := M_Alloc2(nd[idxA]) Ten2Man(a, A) L := make([]float64, 3) M_EigenValsNum(L, a) // SMP derivs and SMP director dndL := la.MatAlloc(3, 3) dNdL := make([]float64, 3) d2ndLdL := utl.Deep3alloc(3, 3, 3) N := make([]float64, 3) F := make([]float64, 3) G := make([]float64, 3) m := SmpDerivs1(dndL, dNdL, N, F, G, L, smp_a, smp_b, smp_β, smp_ϵ) SmpDerivs2(d2ndLdL, L, smp_a, smp_b, smp_β, smp_ϵ, m, N, F, G, dNdL, dndL) n := make([]float64, 3) SmpUnitDirector(n, m, N) // SMP invariants p, q, err := GenInvs(L, n, smp_a) if err != nil { chk.Panic("SmpInvs failed:\n%v", err) } // output io.PfYel("\n\ntst # %d ###################################################################################\n", idxA) io.Pfblue2("L = %v\n", L) io.Pforan("n = %v\n", n) io.Pforan("p = %v\n", p) io.Pforan("q = %v\n", q) // check invariants tvec := make([]float64, 3) GenTvec(tvec, L, n) proj := make([]float64, 3) // projection of tvec along n tdn := la.VecDot(tvec, n) // tvec dot n for i := 0; i < 3; i++ { proj[i] = tdn * n[i] } norm_proj := la.VecNorm(proj) norm_tvec := la.VecNorm(tvec) q_ := GENINVSQEPS + math.Sqrt(norm_tvec*norm_tvec-norm_proj*norm_proj) io.Pforan("proj = %v\n", proj) io.Pforan("norm(proj) = %v == p\n", norm_proj) chk.Scalar(tst, "p", 1e-14, math.Abs(p), norm_proj) chk.Scalar(tst, "q", 1e-13, q, q_) // dt/dL var tmp float64 N_tmp := make([]float64, 3) n_tmp := make([]float64, 3) tvec_tmp := make([]float64, 3) dtdL := la.MatAlloc(3, 3) GenTvecDeriv1(dtdL, L, n, dndL) for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) GenTvec(tvec_tmp, L, n_tmp) L[j] = tmp return tvec_tmp[i] }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("dt/dL[%d][%d]", i, j), dtol, dtdL[i][j], dnum, dver) } } // d²t/dLdL io.Pfpink("\nd²t/dLdL\n") dNdL_tmp := make([]float64, 3) dndL_tmp := la.MatAlloc(3, 3) dtdL_tmp := la.MatAlloc(3, 3) for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { for k := 0; k < 3; k++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[k] = L[k], x m_tmp := SmpDerivs1(dndL_tmp, dNdL_tmp, N_tmp, F, G, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) GenTvecDeriv1(dtdL_tmp, L, n_tmp, dndL_tmp) L[k] = tmp return dtdL_tmp[i][j] }, L[k], 1e-6) dana := GenTvecDeriv2(i, j, k, L, dndL, d2ndLdL[i][j][k]) chk.AnaNum(tst, io.Sf("d²t[%d]/dL[%d]dL[%d]", i, j, k), dtol2, dana, dnum, dver) } } } // change tolerance dtol_tmp := dtol switch idxA { case 5, 11: dtol = 1e-5 case 12: dtol = 0.0013 } // first order derivatives dpdL := make([]float64, 3) dqdL := make([]float64, 3) p_, q_, err := GenInvsDeriv1(dpdL, dqdL, L, n, dndL, smp_a) if err != nil { chk.Panic("%v", err) } chk.Scalar(tst, "p", 1e-17, p, p_) chk.Scalar(tst, "q", 1e-17, q, q_) var ptmp, qtmp float64 io.Pfpink("\ndp/dL\n") for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) ptmp, _, err = GenInvs(L, n_tmp, smp_a) if err != nil { chk.Panic("DerivCentral: SmpInvs failed:\n%v", err) } L[j] = tmp return ptmp }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("dp/dL[%d]", j), dtol, dpdL[j], dnum, dver) } io.Pfpink("\ndq/dL\n") for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) _, qtmp, err = GenInvs(L, n_tmp, smp_a) if err != nil { chk.Panic("DerivCentral: SmpInvs failed:\n%v", err) } L[j] = tmp return qtmp }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("dq/dL[%d]", j), dtol, dqdL[j], dnum, dver) } // recover tolerance dtol = dtol_tmp // change tolerance io.Pforan("dtol2 = %v\n", dtol2) dtol2_tmp := dtol2 switch idxA { case 5: dtol2 = 1e-5 case 10: dtol2 = 0.72 case 11: dtol2 = 1e-5 case 12: dtol2 = 544 } // second order derivatives dpdL_tmp := make([]float64, 3) dqdL_tmp := make([]float64, 3) d2pdLdL := la.MatAlloc(3, 3) d2qdLdL := la.MatAlloc(3, 3) GenInvsDeriv2(d2pdLdL, d2qdLdL, L, n, dpdL, dqdL, p, q, dndL, d2ndLdL, smp_a) io.Pfpink("\nd²p/dLdL\n") for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDerivs1(dndL_tmp, dNdL_tmp, N_tmp, F, G, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) GenInvsDeriv1(dpdL_tmp, dqdL_tmp, L, n_tmp, dndL_tmp, smp_a) L[j] = tmp return dpdL_tmp[i] }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("d²p/dL[%d][%d]", i, j), dtol2, d2pdLdL[i][j], dnum, dver) } } io.Pfpink("\nd²q/dLdL\n") for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDerivs1(dndL_tmp, dNdL_tmp, N_tmp, F, G, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) GenInvsDeriv1(dpdL_tmp, dqdL_tmp, L, n_tmp, dndL_tmp, smp_a) L[j] = tmp return dqdL_tmp[i] }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("d²q/dL[%d][%d]", i, j), dtol2, d2qdLdL[i][j], dnum, dver) } } // recover tolerance dtol2 = dtol2_tmp } }
// add_natbcs_to_jac adds contribution from natural boundary conditions to Jacobian func (o ElemP) add_natbcs_to_jac(sol *Solution) (ok bool) { // clear matrices if o.HasSeep { for i := 0; i < o.Np; i++ { for j := 0; j < o.Nf; j++ { o.Kpf[i][j] = 0 o.Kfp[j][i] = 0 } } la.MatFill(o.Kff, 0) } // compute surface integral nverts := o.Shp.Nverts var shift float64 var ρl, pl, fl, plmax, g, rmp, rmpD float64 var drxdpl, drxdfl, drfdpl, drfdfl float64 for idx, nbc := range o.NatBcs { // plmax shift shift = nbc.Fcn.F(sol.T, nil) // loop over ips of face for jdx, ipf := range o.IpsFace { // interpolation functions and gradients @ face iface := nbc.IdxFace if LogErr(o.Shp.CalcAtFaceIp(o.X, ipf, iface), "add_natbcs_to_jac") { return } Sf := o.Shp.Sf Jf := la.VecNorm(o.Shp.Fnvec) coef := ipf.W * Jf // select natural boundary condition type switch nbc.Key { case "seep": // variables extrapolated to face ρl, pl, fl = o.fipvars(iface, sol) plmax = o.Plmax[idx][jdx] - shift if plmax < 0 { plmax = 0 } // compute derivatives g = pl - plmax // Eq. (24) rmp = o.ramp(fl + o.κ*g) rmpD = o.rampD1(fl + o.κ*g) drxdpl = ρl * o.κ * rmpD // first term in Eq. (A.4) (without Sn) drxdfl = ρl * rmpD // Eq. (A.5) (without Sn) drfdpl = -o.κ * rmpD // Eq. (A.6) (corrected with κ and without Sn) drfdfl = 1.0 - rmpD // Eq. (A.7) (without Sn) for i, m := range o.Shp.FaceLocalV[iface] { μ := o.Vid2seepId[m] for j, n := range o.Shp.FaceLocalV[iface] { ν := o.Vid2seepId[n] o.Kpp[m][n] += coef * Sf[i] * Sf[j] * drxdpl o.Kpf[m][ν] += coef * Sf[i] * Sf[j] * drxdfl o.Kfp[μ][n] += coef * Sf[i] * Sf[j] * drfdpl o.Kff[μ][ν] += coef * Sf[i] * Sf[j] * drfdfl } for n := 0; n < nverts; n++ { // Eqs. (18) and (22) for l, r := range o.Shp.FaceLocalV[iface] { o.Kpp[m][n] += coef * Sf[i] * Sf[l] * o.dρldpl_ex[r][n] * rmp } } } } } } return true }
// Connect connects rod/solid elements in this Rjoint func (o *Rjoint) Connect(cid2elem []Elem, c *inp.Cell) (nnzK int, err error) { // get rod and solid elements rodId := c.JlinId sldId := c.JsldId o.Rod = cid2elem[rodId].(*Rod) o.Sld = cid2elem[sldId].(*ElemU) if o.Rod == nil { err = chk.Err("cannot find joint's rod cell with id == %d", rodId) return } if o.Sld == nil { err = chk.Err("cannot find joint's solid cell with id == %d", sldId) return } // total number of dofs o.Ny = o.Rod.Nu + o.Sld.Nu // material model name matdata := o.Sim.MatParams.Get(o.Edat.Mat) if matdata == nil { err = chk.Err("materials database failed on getting %q material\n", o.Edat.Mat) return } // initialise model err = o.Mdl.Init(matdata.Prms) if err != nil { err = chk.Err("model initialisation failed:\n%v", err) return } // parameters for _, p := range matdata.Prms { switch p.N { case "h": o.h = p.V case "k1": o.k1 = p.V case "k2": o.k2 = p.V case "mu": if p.V > 0.0 { o.Coulomb = true } } } // auxiliary nsig := 2 * o.Ndim // rod data rodH := o.Rod.Cell.Shp rodNp := len(o.Rod.IpsElem) rodNn := rodH.Nverts rodNu := o.Rod.Nu // solid data sldH := o.Sld.Cell.Shp sldS := sldH.S sldNp := len(o.Sld.IpsElem) sldNn := sldH.Nverts sldNu := o.Sld.Nu // shape functions of solid @ nodes of rod o.Nmat = la.MatAlloc(sldNn, rodNn) rodYn := make([]float64, o.Ndim) rodRn := make([]float64, 3) for m := 0; m < rodNn; m++ { for i := 0; i < o.Ndim; i++ { rodYn[i] = o.Rod.X[i][m] } err = sldH.InvMap(rodRn, rodYn, o.Sld.X) if err != nil { return } err = sldH.CalcAtR(o.Sld.X, rodRn, false) if err != nil { return } for n := 0; n < sldNn; n++ { o.Nmat[n][m] = sldH.S[n] } } // coulomb model => σc depends on p values of solid if o.Coulomb { // allocate variables o.Pmat = la.MatAlloc(sldNn, rodNp) o.Emat = la.MatAlloc(sldNn, sldNp) o.rodRp = la.MatAlloc(rodNp, 3) o.σNo = la.MatAlloc(sldNn, nsig) o.σIp = make([]float64, nsig) o.t1 = make([]float64, o.Ndim) o.t2 = make([]float64, o.Ndim) // extrapolator matrix err = sldH.Extrapolator(o.Emat, o.Sld.IpsElem) if err != nil { return } // shape function of solid @ ips of rod for idx, ip := range o.Rod.IpsElem { rodYp := rodH.IpRealCoords(o.Rod.X, ip) err = sldH.InvMap(o.rodRp[idx], rodYp, o.Sld.X) if err != nil { return } err = sldH.CalcAtR(o.Sld.X, o.rodRp[idx], false) if err != nil { return } for n := 0; n < sldNn; n++ { o.Pmat[n][idx] = sldS[n] } } } // joint direction @ ip[idx]; corotational system aligned with rod element o.e0 = la.MatAlloc(rodNp, o.Ndim) o.e1 = la.MatAlloc(rodNp, o.Ndim) o.e2 = la.MatAlloc(rodNp, o.Ndim) π := make([]float64, o.Ndim) // Eq. (27) Q := la.MatAlloc(o.Ndim, o.Ndim) α := 666.0 Jvec := rodH.Jvec3d[:o.Ndim] 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 } // compute basis vectors J := rodH.J π[0] = Jvec[0] + α π[1] = Jvec[1] e0[0] = Jvec[0] / J e0[1] = Jvec[1] / J if o.Ndim == 3 { π[2] = Jvec[2] e0[2] = Jvec[2] / J } la.MatSetDiag(Q, 1) la.VecOuterAdd(Q, -1, e0, e0) // Q := I - e0 dyad e0 la.MatVecMul(e1, 1, Q, π) // Eq. (29) * norm(E1) la.VecScale(e1, 0, 1.0/la.VecNorm(e1), e1) if o.Ndim == 3 { e2[0] = e0[1]*e1[2] - e0[2]*e1[1] e2[1] = e0[2]*e1[0] - e0[0]*e1[2] e2[2] = e0[0]*e1[1] - e0[1]*e1[0] } // compute auxiliary tensors if o.Coulomb { e1_dy_e1 := tsr.Alloc2() e2_dy_e2 := tsr.Alloc2() for i := 0; i < o.Ndim; i++ { for j := 0; j < o.Ndim; j++ { e1_dy_e1[i][j] = e1[i] * e1[j] e2_dy_e2[i][j] = e2[i] * e2[j] } } } } // auxiliary variables o.ΔuC = la.MatAlloc(rodNn, o.Ndim) o.Δw = make([]float64, o.Ndim) o.qb = make([]float64, o.Ndim) o.fC = make([]float64, rodNu) // temporary Jacobian matrices. see Eq. (57) o.Krr = la.MatAlloc(rodNu, rodNu) o.Krs = la.MatAlloc(rodNu, sldNu) o.Ksr = la.MatAlloc(sldNu, rodNu) o.Kss = la.MatAlloc(sldNu, sldNu) // debugging //if true { if false { o.debug_print_init() } // success return o.Ny * o.Ny, nil }
func Test_smpinvs02(tst *testing.T) { //verbose() chk.PrintTitle("smpinvs02") // coefficients for smp invariants smp_a := -1.0 smp_b := 0.5 smp_β := 1e-1 // derivative values become too high with smp_ϵ := 1e-1 // small β and ϵ @ zero // constants for checking derivatives dver := chk.Verbose dtol := 1e-9 dtol2 := 1e-8 // run tests nd := test_nd for idxA := 0; idxA < len(test_nd); idxA++ { //for idxA := 0; idxA < 1; idxA++ { //for idxA := 10; idxA < 11; idxA++ { // tensor and eigenvalues A := test_AA[idxA] a := M_Alloc2(nd[idxA]) Ten2Man(a, A) L := make([]float64, 3) M_EigenValsNum(L, a) // SMP director N := make([]float64, 3) n := make([]float64, 3) m := SmpDirector(N, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n, m, N) // output io.PfYel("\n\ntst # %d ###################################################################################\n", idxA) io.Pforan("L = %v\n", L) io.Pforan("N = %v\n", N) io.Pforan("m = %v\n", m) io.Pfpink("n = %v\n", n) chk.Vector(tst, "L", 1e-12, L, test_λ[idxA]) chk.Scalar(tst, "norm(n)==1", 1e-15, la.VecNorm(n), 1) chk.Scalar(tst, "m=norm(N)", 1e-14, m, la.VecNorm(N)) // dN/dL var tmp float64 N_tmp := make([]float64, 3) dNdL := make([]float64, 3) SmpDirectorDeriv1(dNdL, L, smp_a, smp_b, smp_β, smp_ϵ) io.Pfpink("\ndNdL = %v\n", dNdL) for i := 0; i < 3; i++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[i] = L[i], x SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) L[i] = tmp return N_tmp[i] }, L[i], 1e-6) chk.AnaNum(tst, io.Sf("dN/dL[%d][%d]", i, i), dtol, dNdL[i], dnum, dver) } // dm/dL n_tmp := make([]float64, 3) dmdL := make([]float64, 3) SmpNormDirectorDeriv1(dmdL, m, N, dNdL) io.Pfpink("\ndmdL = %v\n", dmdL) for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) L[j] = tmp return m_tmp }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("dm/dL[%d]", j), dtol, dmdL[j], dnum, dver) } // dn/dL dndL := la.MatAlloc(3, 3) SmpUnitDirectorDeriv1(dndL, m, N, dNdL, dmdL) io.Pfpink("\ndndL = %v\n", dndL) for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpUnitDirector(n_tmp, m_tmp, N_tmp) L[j] = tmp return n_tmp[i] }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("dn/dL[%d][%d]", i, j), dtol, dndL[i][j], dnum, dver) } } // change tolerance dtol2_tmp := dtol2 if idxA == 10 || idxA == 11 { dtol2 = 1e-6 } // d²m/dLdL dNdL_tmp := make([]float64, 3) dmdL_tmp := make([]float64, 3) d2NdL2 := make([]float64, 3) d2mdLdL := la.MatAlloc(3, 3) SmpDirectorDeriv2(d2NdL2, L, smp_a, smp_b, smp_β, smp_ϵ) SmpNormDirectorDeriv2(d2mdLdL, L, smp_a, smp_b, smp_β, smp_ϵ, m, N, dNdL, d2NdL2, dmdL) io.Pfpink("\nd2mdLdL = %v\n", d2mdLdL) for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[j] = L[j], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpDirectorDeriv1(dNdL_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpNormDirectorDeriv1(dmdL_tmp, m_tmp, N_tmp, dNdL_tmp) L[j] = tmp return dmdL_tmp[i] }, L[j], 1e-6) chk.AnaNum(tst, io.Sf("d2m/dL[%d]dL[%d]", i, j), dtol2, d2mdLdL[i][j], dnum, dver) } } // d²N/dLdL io.Pfpink("\nd²N/dLdL\n") for i := 0; i < 3; i++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[i] = L[i], x SmpDirectorDeriv1(dNdL_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) L[i] = tmp return dNdL_tmp[i] }, L[i], 1e-6) chk.AnaNum(tst, io.Sf("d²N[%d]/dL[%d]dL[%d]", i, i, i), dtol2, d2NdL2[i], dnum, dver) } // d²n/dLdL io.Pfpink("\nd²n/dLdL\n") dndL_tmp := la.MatAlloc(3, 3) d2ndLdL := utl.Deep3alloc(3, 3, 3) SmpUnitDirectorDeriv2(d2ndLdL, m, N, dNdL, d2NdL2, dmdL, n, d2mdLdL, dndL) for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { for k := 0; k < 3; k++ { dnum, _ := num.DerivCentral(func(x float64, args ...interface{}) (res float64) { tmp, L[k] = L[k], x m_tmp := SmpDirector(N_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpDirectorDeriv1(dNdL_tmp, L, smp_a, smp_b, smp_β, smp_ϵ) SmpNormDirectorDeriv1(dmdL_tmp, m_tmp, N_tmp, dNdL_tmp) SmpUnitDirectorDeriv1(dndL_tmp, m_tmp, N_tmp, dNdL_tmp, dmdL_tmp) L[k] = tmp return dndL_tmp[i][j] }, L[k], 1e-6) chk.AnaNum(tst, io.Sf("d²n[%d]/dL[%d]dL[%d]", i, j, k), dtol2, d2ndLdL[i][j][k], dnum, dver) } } } // recover tolerance dtol2 = dtol2_tmp // SMP derivs //if false { if true { io.Pfpink("\nSMP derivs\n") dndL_ := la.MatAlloc(3, 3) dNdL_ := make([]float64, 3) d2ndLdL_ := utl.Deep3alloc(3, 3, 3) N_ := make([]float64, 3) F_ := make([]float64, 3) G_ := make([]float64, 3) m_ := SmpDerivs1(dndL_, dNdL_, N_, F_, G_, L, smp_a, smp_b, smp_β, smp_ϵ) SmpDerivs2(d2ndLdL_, L, smp_a, smp_b, smp_β, smp_ϵ, m_, N_, F_, G_, dNdL_, dndL_) chk.Scalar(tst, "m_", 1e-14, m_, m) chk.Vector(tst, "N_", 1e-15, N_, N) chk.Vector(tst, "dNdL_", 1e-15, dNdL_, dNdL) chk.Matrix(tst, "dndL_", 1e-13, dndL_, dndL) chk.Deep3(tst, "d2ndLdL_", 1e-11, d2ndLdL_, d2ndLdL) } } }
// contact_add_to_jac adds coupled equations due to contact modelling to Jacobian func (o *ElemU) contact_add_to_jac(Kb *la.Triplet, sol *Solution) (err error) { // clear matrices for i := 0; i < o.Nq; i++ { for j := 0; j < o.Nq; j++ { o.Kqq[i][j] = 0 } for j := 0; j < o.Nu; j++ { o.Kqu[i][j] = 0 o.Kuq[j][i] = 0 } } // compute surface integral var qb, db, Hb float64 dddu := make([]float64, o.Ndim) for _, nbc := range o.NatBcs { // loop over ips of face for _, ipf := range o.IpsFace { // contact switch nbc.Key { case "contact": // 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 coef := ipf[3] * o.Thickness Jf := la.VecNorm(nvec) // variables extrapolated to face qb = o.fipvars(iface, sol) xf := o.Cell.Shp.FaceIpRealCoords(o.X, ipf, iface) la.VecAdd(xf, 1, o.us) // add displacement: x = X + u db = o.contact_g(xf) o.contact_dgdx(dddu, xf) // compute derivatives Hb = o.contact_rampD1(qb + o.κ*db) for i, m := range o.Cell.Shp.FaceLocalVerts[iface] { μ := o.Vid2contactId[m] for j, n := range o.Cell.Shp.FaceLocalVerts[iface] { ν := o.Vid2contactId[n] o.Kqq[μ][ν] += coef * Jf * Sf[i] * Sf[j] * (1.0 - Hb) for k := 0; k < o.Ndim; k++ { r := k + m*o.Ndim c := k + n*o.Ndim val := coef * Sf[i] * Sf[j] * Hb * o.κ * dddu[k] * Jf o.Kuq[r][ν] += coef * Sf[i] * Sf[j] * Hb * nvec[k] o.Kqu[μ][c] -= val o.K[r][c] += val } } } } } } // add Ks to sparse matrix Kb for i, I := range o.Qmap { for j, J := range o.Qmap { Kb.Put(I, J, o.Kqq[i][j]) } for j, J := range o.Umap { Kb.Put(I, J, o.Kqu[i][j]) Kb.Put(J, I, o.Kuq[j][i]) } } for i, I := range o.Umap { for j, J := range o.Umap { Kb.Put(I, J, o.K[i][j]) } } return }