// explicit Runge-Kutta step function func erk_step(o *ODE, y []float64, x float64, args ...interface{}) (rerr float64, err error) { for i := 0; i < o.nstg; i++ { o.u[i] = x + o.h*o.erkdat.c[i] la.VecCopy(o.v[i], 1, y) for j := 0; j < i; j++ { la.VecAdd(o.v[i], o.h*o.erkdat.a[i][j], o.f[j]) } if i == 0 && o.erkdat.usefp && !o.first { la.VecCopy(o.f[i], 1, o.f[o.nstg-1]) } else { o.nfeval += 1 err = o.fcn(o.f[i], o.u[i], o.v[i], args...) if err != nil { return } } } var lerrm float64 // m component of local error estimate for m := 0; m < o.ndim; m++ { lerrm = 0.0 for i := 0; i < o.nstg; i++ { o.w[0][m] += o.erkdat.b[i] * o.f[i][m] * o.h lerrm += (o.erkdat.be[i] - o.erkdat.b[i]) * o.f[i][m] * o.h } rerr += math.Pow(lerrm/o.scal[m], 2.0) } rerr = max(math.Sqrt(rerr/float64(o.ndim)), 1.0e-10) return }
// 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 }
// 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 }