def D_map(A): """Unit stiffness tensor mapping strain -> stress.""" return lambda_ * ufl.tr(A) * ufl.Identity(3) + 2 * mu * A
def sigma_u(u): """Consitutive relation for stress-strain. Assuming plane-stress in XY""" eps = 0.5 * (ufl.grad(u) + ufl.grad(u).T) sigma = E / (1. - nu**2) * ( (1. - nu) * eps + nu * ufl.Identity(2) * ufl.tr(eps)) return sigma
# Define state and rate as (ordered) list of functions m, mt, mtt, δm = [u], [ut], [utt], [δu] # Time integrator odeint = dolfiny.odeint.ODEInt2(t=time, dt=dt, x=m, xt=mt, xtt=mtt, rho=0.95) # Configuration gradient I = ufl.Identity(u.geometric_dimension()) # noqa: E741 F = I + ufl.grad(u) # deformation gradient as function of displacement # Strain measures # E = E(u) total strain E = 1 / 2 * (F.T * F - I) # S = S(E) stress S = 2 * mu * E + la * ufl.tr(E) * I # Variation of rate of Green-Lagrange strain δE = dolfiny.expression.derivative(E, m, δm) # Weak form (as one-form) f = ufl.inner(δu, rho * utt) * dx + ufl.inner(δu, eta * ut) * dx \ + ufl.inner(δE, S) * dx \ - ufl.inner(δu, rho * b) * dx # Optional: linearise weak form # f = dolfiny.expression.linearise(f, m) # linearise around zero state # Overall form (as one-form) F = odeint.discretise_in_time(f) # Overall form (as list of forms)
def sigma(self, u): n = u.geometric_dimension() lmbda = self.lmbda3D(0) mu = self.mu3D(0) return lmbda * tr(self.eps(u)) * Identity(n) + 2 * mu * self.eps(u)
def test_diff_then_integrate(): # Define 1D geometry n = 21 mesh = UnitIntervalMesh(MPI.COMM_WORLD, n) # Shift and scale mesh x0, x1 = 1.5, 3.14 mesh.coordinates()[:] *= (x1 - x0) mesh.coordinates()[:] += x0 x = SpatialCoordinate(mesh)[0] xs = 0.1 + 0.8 * x / x1 # scaled to be within [0.1,0.9] # Define list of expressions to test, and configure # accuracies these expressions are known to pass with. # The reason some functions are less accurately integrated is # likely that the default choice of quadrature rule is not perfect F_list = [] def reg(exprs, acc=10): for expr in exprs: F_list.append((expr, acc)) # FIXME: 0*dx and 1*dx fails in the ufl-ffcx-jit framework somewhere # reg([Constant(0.0, cell=cell)]) # reg([Constant(1.0, cell=cell)]) monomial_list = [x**q for q in range(2, 6)] reg(monomial_list) reg([2.3 * p + 4.5 * q for p in monomial_list for q in monomial_list]) reg([x**x]) reg([x**(x**2)], 8) reg([x**(x**3)], 6) reg([x**(x**4)], 2) # Special functions: reg([atan(xs)], 8) reg([sin(x), cos(x), exp(x)], 5) reg([ln(xs), pow(x, 2.7), pow(2.7, x)], 3) reg([asin(xs), acos(xs)], 1) reg([tan(xs)], 7) try: import scipy except ImportError: scipy = None if hasattr(math, 'erf') or scipy is not None: reg([erf(xs)]) else: print( "Warning: skipping test of erf, old python version and no scipy.") # if 0: # print("Warning: skipping tests of bessel functions, doesn't build on all platforms.") # elif scipy is None: # print("Warning: skipping tests of bessel functions, missing scipy.") # else: # for nu in (0, 1, 2): # # Many of these are possibly more accurately integrated, # # but 4 covers all and is sufficient for this test # reg([bessel_J(nu, xs), bessel_Y(nu, xs), bessel_I(nu, xs), bessel_K(nu, xs)], 4) # To handle tensor algebra, make an x dependent input tensor # xx and square all expressions def reg2(exprs, acc=10): for expr in exprs: F_list.append((inner(expr, expr), acc)) xx = as_matrix([[2 * x**2, 3 * x**3], [11 * x**5, 7 * x**4]]) x3v = as_vector([3 * x**2, 5 * x**3, 7 * x**4]) cc = as_matrix([[2, 3], [4, 5]]) reg2([xx]) reg2([x3v]) reg2([cross(3 * x3v, as_vector([-x3v[1], x3v[0], x3v[2]]))]) reg2([xx.T]) reg2([tr(xx)]) reg2([det(xx)]) reg2([dot(xx, 0.1 * xx)]) reg2([outer(xx, xx.T)]) reg2([dev(xx)]) reg2([sym(xx)]) reg2([skew(xx)]) reg2([elem_mult(7 * xx, cc)]) reg2([elem_div(7 * xx, xx + cc)]) reg2([elem_pow(1e-3 * xx, 1e-3 * cc)]) reg2([elem_pow(1e-3 * cc, 1e-3 * xx)]) reg2([elem_op(lambda z: sin(z) + 2, 0.03 * xx)], 2) # pretty inaccurate... # FIXME: Add tests for all UFL operators: # These cause discontinuities and may be harder to test in the # above fashion: # 'inv', 'cofac', # 'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not', # 'conditional', 'sign', # 'jump', 'avg', # 'LiftingFunction', 'LiftingOperator', # FIXME: Test other derivatives: (but algorithms for operator # derivatives are the same!): # 'variable', 'diff', # 'Dx', 'grad', 'div', 'curl', 'rot', 'Dn', 'exterior_derivative', # Run through all operators defined above and compare integrals debug = 0 for F, acc in F_list: # Apply UFL differentiation f = diff(F, SpatialCoordinate(mesh))[..., 0] if debug: print(F) print(x) print(f) # Apply integration with DOLFINx # (also passes through form compilation and jit) M = f * dx f_integral = assemble_scalar(M) # noqa f_integral = mesh.mpi_comm().allreduce(f_integral, op=MPI.SUM) # Compute integral of f manually from anti-derivative F # (passes through pybind11 interface and uses UFL evaluation) F_diff = F((x1, )) - F((x0, )) # Compare results. Using custom relative delta instead # of decimal digits here because some numbers are >> 1. delta = min(abs(f_integral), abs(F_diff)) * 10**-acc assert f_integral - F_diff <= delta
def sigma0(self): n = self.dim lmbda = self.lmbda3D(0) mu = self.mu3D(0) return lmbda * tr(self.eps0()) * Identity(n) + 2 * mu * self.eps0()
def elastic_energy_density(self, eps): lmbda = self.E0 * self.nu / ((1.0 + self.nu) * (1.0 - 2.0 * self.nu)) mu = self.E0 / (2.0 * (1.0 + self.nu)) return 1.0 / 2.0 * lmbda * tr(eps)**2 + mu * inner(eps, eps)
def test_neohooke(): mesh = dolfinx.mesh.create_unit_cube(MPI.COMM_WORLD, 7, 7, 7) V = dolfinx.fem.VectorFunctionSpace(mesh, ("P", 1)) P = dolfinx.fem.FunctionSpace(mesh, ("P", 1)) L = dolfinx.fem.FunctionSpace(mesh, ("DG", 0)) u = dolfinx.fem.Function(V, name="u") v = ufl.TestFunction(V) p = dolfinx.fem.Function(P, name="p") q = ufl.TestFunction(P) lmbda0 = dolfinx.fem.Function(L) d = mesh.topology.dim Id = ufl.Identity(d) F = Id + ufl.grad(u) C = F.T * F J = ufl.det(F) E_, nu_ = 10.0, 0.3 mu, lmbda = E_ / (2 * (1 + nu_)), E_ * nu_ / ((1 + nu_) * (1 - 2 * nu_)) psi = (mu / 2) * (ufl.tr(C) - 3) - mu * ufl.ln(J) + lmbda / 2 * ufl.ln(J)**2 + (p - 1.0)**2 pi = psi * ufl.dx F0 = ufl.derivative(pi, u, v) F1 = ufl.derivative(pi, p, q) # Number of eigenvalues to find nev = 8 opts = PETSc.Options("neohooke") opts["eps_smallest_magnitude"] = True opts["eps_nev"] = nev opts["eps_ncv"] = 50 * nev opts["eps_conv_abs"] = True # opts["eps_non_hermitian"] = True opts["eps_tol"] = 1.0e-14 opts["eps_max_it"] = 1000 opts["eps_error_relative"] = "ascii::ascii_info_detail" opts["eps_monitor"] = "ascii" slepcp = dolfiny.slepcblockproblem.SLEPcBlockProblem([F0, F1], [u, p], lmbda0, prefix="neohooke") slepcp.solve() # mat = dolfiny.la.petsc_to_scipy(slepcp.eps.getOperators()[0]) # eigvals, eigvecs = linalg.eigsh(mat, which="SM", k=nev) with dolfinx.io.XDMFFile(MPI.COMM_WORLD, "eigvec.xdmf", "w") as ofile: ofile.write_mesh(mesh) for i in range(nev): eigval, ur, ui = slepcp.getEigenpair(i) # Expect first 6 eignevalues 0, i.e. rigid body modes if i < 6: assert np.isclose(eigval, 0.0) for func in ur: name = func.name func.name = f"{name}_eigvec_{i}_real" ofile.write_function(func) func.name = name
def sigma(v): return (2.0 * mu * epsilon(v) + lmbda * tr(epsilon(v)) * Identity(len(v)))
def skw(tau): """Define vectorized skew operator""" sk = 2 * skew(tau) return as_vector((sk[0, 1], sk[0, 2], sk[1, 2])) cell = tetrahedron n = 3 # Finite element exterior calculus syntax r = 1 S = VectorElement("P Lambda", cell, r, form_degree=n - 1) V = VectorElement("P Lambda", cell, r - 1, form_degree=n) Q = VectorElement("P Lambda", cell, r - 1, form_degree=n) # Alternative syntax: # S = VectorElement("BDM", cell, r) # V = VectorElement("Discontinuous Lagrange", cell, r-1) # Q = VectorElement("Discontinuous Lagrange", cell, r-1) W = MixedElement(S, V, Q) (sigma, u, gamma) = TrialFunctions(W) (tau, v, eta) = TestFunctions(W) a = (inner(sigma, tau) - tr(sigma) * tr(tau) + dot(div(tau), u) - dot(div(sigma), v) + inner(skw(tau), gamma) + inner(skw(sigma), eta)) * dx
# Define state and rate as (ordered) list of functions m, mt, mtt, δm = [u, S], [ut, St], [utt, Stt], [δu, δS] # Time integrator odeint = dolfiny.odeint.ODEInt2(t=time, dt=dt, x=m, xt=mt, xtt=mtt, rho=0.95) # Configuration gradient I = ufl.Identity(u.geometric_dimension()) # noqa: E741 F = I + ufl.grad(u) # deformation gradient as function of displacement # Strain measures # E = E(u) total strain E = 1 / 2 * (F.T * F - I) # E = E(S) elastic strain Es = 1 / (2 * mu) * S - la / (2 * mu * (3 * la + 2 * mu)) * ufl.tr(S) * I # Variation of rate of Green-Lagrange strain δE = dolfiny.expression.derivative(E, m, δm) # Weak form (as one-form) f = ufl.inner(δu, rho * utt) * dx + ufl.inner(δu, eta * ut) * dx \ + ufl.inner(δE, S) * dx + ufl.inner(δS, Es - E) * dx \ - ufl.inner(δu, rho * b) * dx # Optional: linearise weak form # f = dolfiny.expression.linearise(f, m) # linearise around zero state # Overall form (as one-form) F = odeint.discretise_in_time(f) # Overall form (as list of forms)
top_facets = dolfinx.mesh.locate_entities_boundary(mesh, 1, top) top_dofs = dolfinx.fem.locate_dofs_topological(V_u, mesh.topology.dim - 1, top_facets) bottom_facets = dolfinx.mesh.locate_entities_boundary(mesh, 1, bottom) bottom_dofs = dolfinx.fem.locate_dofs_topological(V_u, mesh.topology.dim - 1, bottom_facets) # energy mu = parameters["model"]["mu"] lmbda = parameters["model"]["lmbda"] def _e(u): return ufl.sym(ufl.grad(u)) en_density = 1/2 * (2*mu* ufl.inner(_e(u),_e(u))) + lmbda*ufl.tr(_e(u))**2 energy = en_density * dx - ufl.inner(u, g)*dS(4) #bcs = [dirichletbc(zero, bottom_dofs), dirichletbc(one, top_dofs)] bcs = [dirichletbc(zero, bottom_dofs)] # solving from solvers import SNESSolver D_energy_u = ufl.derivative(energy, u, ufl.TestFunction(V_u)) problem = SNESSolver( D_energy_u, u, bcs, bounds=None, petsc_options=parameters.get("solvers").get("snes"),
def D_rebar_map(A): """Unit stiffness tensor (rebars) mapping strain -> stress.""" return lambda_rebar_ * ufl.tr(A) * ufl.Identity(3) + 2 * mu_rebar * A
def C_map(A): """unit compliance tensor mapping stress -> strain.""" return -lambda_ / (2.0 * mu * (3.0 * lambda_ + 2.0 * mu) ) * ufl.tr(A) * ufl.Identity(3) + 1.0 / (2 * mu) * A
def sigma(v): return (1/(1+Nu))*strain(v) + ((1*Nu)/((1+Nu)*(1-2*Nu)))*ufl.tr(strain(v))*ufl.Identity(v.geometric_dimension()) #v.cell().d
"""Square root of J2 invariant of tensor A""" J2 = 1 / 2 * ufl.inner(A, A) rJ2 = ufl.sqrt(J2) return ufl.conditional(rJ2 < 1.0e-12, 0.0, rJ2) # Configuration gradient I = ufl.Identity(u.geometric_dimension()) # noqa: E741 F = I + ufl.grad(u) # deformation gradient as function of displacement # Strain measures E = 1 / 2 * (F.T * F - I) # E = E(F), total Green-Lagrange strain E_el = E - P # E_el = E(F) - P, elastic strain # Stress S = 2 * mu * E_el + la * ufl.tr( E_el) * I # S = S(E_el), PK2, St.Venant-Kirchhoff # Wrap variable around expression (for diff) S, B, h = ufl.variable(S), ufl.variable(B), ufl.variable(h) # Yield function f = ufl.sqrt(3) * rJ2(ufl.dev(S) - ufl.dev(B)) - (Sy + h) # Plastic potential g = f # Total differential of yield function df = + ufl.inner(ufl.diff(f, S), S - S0) \ + ufl.inner(ufl.diff(f, h), h - h0) \ + ufl.inner(ufl.diff(f, B), B - B0)
def sigma(w, gdim): return 2.0 * mu * ufl.sym(grad(w)) + lmbda * ufl.tr( grad(w)) * ufl.Identity(gdim)
def sigma(v): return (2.0 * mu * ufl.sym(ufl.grad(v)) + lmbda * ufl.tr(ufl.sym(ufl.grad(v))) * ufl.Identity(len(v)))
def elastic_energy_density(self, eps, alpha): # lmbda = self.lmbda3D(alpha) lmbda = self.lmbda2D(alpha) mu = self.mu3D(alpha) return 1.0 / 2.0 * lmbda * tr(eps - self.eps0())**2 + mu * inner( eps - self.eps0(), eps - self.eps0())
def compliance(sigma): ' Returns the strain tensor as a function of sigma ' return sigma/(2*mu) - lmbda/(4*mu*(lmbda + mu))*tr(sigma)*Identity(u.geometric_dimension())
def elastic_energy_density(self, eps, alpha): lmbda = self.lmbda3D(alpha) mu = self.mu3D(alpha) return 1.0 / 2.0 * lmbda * tr(eps)**2 + mu * inner(eps, eps)
def write_output(self, pb, writemesh=False, N=1, t=0): if writemesh: if self.write_results_every > 0: self.resultsfiles = {} for res in self.results_to_write: outfile = XDMFFile(self.comm, self.output_path+'/results_'+pb.simname+'_'+res+'.xdmf', 'w') outfile.write_mesh(self.mesh) self.resultsfiles[res] = outfile return else: # write results every write_results_every steps if self.write_results_every > 0 and N % self.write_results_every == 0: # save solution to XDMF format for res in self.results_to_write: if res=='displacement': self.resultsfiles[res].write_function(pb.u, t) elif res=='velocity': # passed in v is not a function but form, so we have to project v_proj = project(pb.vel, pb.V_u, pb.dx_, nm="Velocity") self.resultsfiles[res].write_function(v_proj, t) elif res=='acceleration': # passed in a is not a function but form, so we have to project a_proj = project(pb.acc, pb.V_u, pb.dx_, nm="Acceleration") self.resultsfiles[res].write_function(a_proj, t) elif res=='pressure': self.resultsfiles[res].write_function(pb.p, t) elif res=='cauchystress': stressfuncs=[] for n in range(pb.num_domains): stressfuncs.append(pb.ma[n].sigma(pb.u,pb.p,ivar=pb.internalvars)) cauchystress = project(stressfuncs, pb.Vd_tensor, pb.dx_, nm="CauchyStress") self.resultsfiles[res].write_function(cauchystress, t) elif res=='trmandelstress': stressfuncs=[] for n in range(pb.num_domains): stressfuncs.append(tr(pb.ma[n].M(pb.u,pb.p,ivar=pb.internalvars))) trmandelstress = project(stressfuncs, pb.Vd_scalar, pb.dx_, nm="trMandelStress") self.resultsfiles[res].write_function(trmandelstress, t) elif res=='trmandelstress_e': stressfuncs=[] for n in range(pb.num_domains): if pb.mat_growth[n]: stressfuncs.append(tr(pb.ma[n].M_e(pb.u,pb.p,pb.ki.C(pb.u),ivar=pb.internalvars))) else: stressfuncs.append(as_ufl(0)) trmandelstress_e = project(stressfuncs, pb.Vd_scalar, pb.dx_, nm="trMandelStress_e") self.resultsfiles[res].write_function(trmandelstress_e, t) elif res=='vonmises_cauchystress': stressfuncs=[] for n in range(pb.num_domains): stressfuncs.append(pb.ma[n].sigma_vonmises(pb.u,pb.p,ivar=pb.internalvars)) vonmises_cauchystress = project(stressfuncs, pb.Vd_scalar, pb.dx_, nm="vonMises_CauchyStress") self.resultsfiles[res].write_function(vonmises_cauchystress, t) elif res=='pk1stress': stressfuncs=[] for n in range(pb.num_domains): stressfuncs.append(pb.ma[n].P(pb.u,pb.p,ivar=pb.internalvars)) pk1stress = project(stressfuncs, pb.Vd_tensor, pb.dx_, nm="PK1Stress") self.resultsfiles[res].write_function(pk1stress, t) elif res=='pk2stress': stressfuncs=[] for n in range(pb.num_domains): stressfuncs.append(pb.ma[n].S(pb.u,pb.p,ivar=pb.internalvars)) pk2stress = project(stressfuncs, pb.Vd_tensor, pb.dx_, nm="PK2Stress") self.resultsfiles[res].write_function(pk2stress, t) elif res=='jacobian': jacobian = project(pb.ki.J(pb.u), pb.Vd_scalar, pb.dx_, nm="Jacobian") self.resultsfiles[res].write_function(jacobian, t) elif res=='glstrain': glstrain = project(pb.ki.E(pb.u), pb.Vd_tensor, pb.dx_, nm="GreenLagrangeStrain") self.resultsfiles[res].write_function(glstrain, t) elif res=='eastrain': eastrain = project(pb.ki.e(pb.u), pb.Vd_tensor, pb.dx_, nm="EulerAlmansiStrain") self.resultsfiles[res].write_function(eastrain, t) elif res=='fiberstretch': fiberstretch = project(pb.ki.fibstretch(pb.u,pb.fib_func[0]), pb.Vd_scalar, pb.dx_, nm="FiberStretch") self.resultsfiles[res].write_function(fiberstretch, t) elif res=='fiberstretch_e': stretchfuncs=[] for n in range(pb.num_domains): if pb.mat_growth[n]: stretchfuncs.append(pb.ma[n].fibstretch_e(pb.ki.C(pb.u),pb.theta,pb.fib_func[0])) else: stretchfuncs.append(as_ufl(0)) fiberstretch_e = project(stretchfuncs, pb.Vd_scalar, pb.dx_, nm="FiberStretch_e") self.resultsfiles[res].write_function(fiberstretch_e, t) elif res=='theta': self.resultsfiles[res].write_function(pb.theta, t) elif res=='phi_remod': phifuncs=[] for n in range(pb.num_domains): if pb.mat_remodel[n]: phifuncs.append(pb.ma[n].phi_remod(pb.theta)) else: phifuncs.append(as_ufl(0)) phiremod = project(phifuncs, pb.Vd_scalar, pb.dx_, nm="phiRemodel") self.resultsfiles[res].write_function(phiremod, t) elif res=='tau_a': self.resultsfiles[res].write_function(pb.tau_a, t) elif res=='fiber1': fiber1 = project(pb.fib_func[0], pb.Vd_vector, pb.dx_, nm="Fiber1") self.resultsfiles[res].write_function(fiber1, t) elif res=='fiber2': fiber2 = project(pb.fib_func[1], pb.Vd_vector, pb.dx_, nm="Fiber2") self.resultsfiles[res].write_function(fiber2, t) else: raise NameError("Unknown output to write for solid mechanics!") if self.write_restart_every > 0 and N % self.write_restart_every == 0: self.writecheckpoint(pb, N)
def test_div_grad_then_integrate_over_cells_and_boundary(): # Define 2D geometry n = 10 mesh = RectangleMesh( [numpy.array([0.0, 0.0, 0.0]), numpy.array([2.0, 3.0, 0.0])], 2 * n, 3 * n) x, y = SpatialCoordinate(mesh) xs = 0.1 + 0.8 * x / 2 # scaled to be within [0.1,0.9] # ys = 0.1 + 0.8 * y / 3 # scaled to be within [0.1,0.9] n = FacetNormal(mesh) # Define list of expressions to test, and configure accuracies # these expressions are known to pass with. The reason some # functions are less accurately integrated is likely that the # default choice of quadrature rule is not perfect F_list = [] def reg(exprs, acc=10): for expr in exprs: F_list.append((expr, acc)) # FIXME: 0*dx and 1*dx fails in the ufl-ffcx-jit framework somewhere # reg([Constant(0.0, cell=cell)]) # reg([Constant(1.0, cell=cell)]) monomial_list = [x**q for q in range(2, 6)] reg(monomial_list) reg([2.3 * p + 4.5 * q for p in monomial_list for q in monomial_list]) reg([xs**xs]) reg( [xs**(xs**2)], 8 ) # Note: Accuracies here are from 1D case, not checked against 2D results. reg([xs**(xs**3)], 6) reg([xs**(xs**4)], 2) # Special functions: reg([atan(xs)], 8) reg([sin(x), cos(x), exp(x)], 5) reg([ln(xs), pow(x, 2.7), pow(2.7, x)], 3) reg([asin(xs), acos(xs)], 1) reg([tan(xs)], 7) # To handle tensor algebra, make an x dependent input tensor # xx and square all expressions def reg2(exprs, acc=10): for expr in exprs: F_list.append((inner(expr, expr), acc)) xx = as_matrix([[2 * x**2, 3 * x**3], [11 * x**5, 7 * x**4]]) xxs = as_matrix([[2 * xs**2, 3 * xs**3], [11 * xs**5, 7 * xs**4]]) x3v = as_vector([3 * x**2, 5 * x**3, 7 * x**4]) cc = as_matrix([[2, 3], [4, 5]]) reg2( [xx] ) # TODO: Make unit test for UFL from this, results in listtensor with free indices reg2([x3v]) reg2([cross(3 * x3v, as_vector([-x3v[1], x3v[0], x3v[2]]))]) reg2([xx.T]) reg2([tr(xx)]) reg2([det(xx)]) reg2([dot(xx, 0.1 * xx)]) reg2([outer(xx, xx.T)]) reg2([dev(xx)]) reg2([sym(xx)]) reg2([skew(xx)]) reg2([elem_mult(7 * xx, cc)]) reg2([elem_div(7 * xx, xx + cc)]) reg2([elem_pow(1e-3 * xxs, 1e-3 * cc)]) reg2([elem_pow(1e-3 * cc, 1e-3 * xx)]) reg2([elem_op(lambda z: sin(z) + 2, 0.03 * xx)], 2) # pretty inaccurate... # FIXME: Add tests for all UFL operators: # These cause discontinuities and may be harder to test in the # above fashion: # 'inv', 'cofac', # 'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not', # 'conditional', 'sign', # 'jump', 'avg', # 'LiftingFunction', 'LiftingOperator', # FIXME: Test other derivatives: (but algorithms for operator # derivatives are the same!): # 'variable', 'diff', # 'Dx', 'grad', 'div', 'curl', 'rot', 'Dn', 'exterior_derivative', # Run through all operators defined above and compare integrals debug = 0 if debug: F_list = F_list[1:] for F, acc in F_list: if debug: print('\n', "F:", str(F)) # Integrate over domain and its boundary int_dx = assemble(div(grad(F)) * dx(mesh)) # noqa int_ds = assemble(dot(grad(F), n) * ds(mesh)) # noqa if debug: print(int_dx, int_ds) # Compare results. Using custom relative delta instead of # decimal digits here because some numbers are >> 1. delta = min(abs(int_dx), abs(int_ds)) * 10**-acc assert int_dx - int_ds <= delta
# Functions u = Coefficient(element) # Displacement from previous iteration # B = Coefficient(element) # Body force per unit volume # T = Coefficient(element) # Traction force on the boundary # Now, we can define the kinematic quantities involved in the model:: # Kinematics d = len(u) I = Identity(d) # Identity tensor F = variable(I + grad(u)) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor # Invariants of deformation tensors Ic = tr(C) J = det(F) # Before defining the energy density and thus the total potential # energy, it only remains to specify constants for the elasticity # parameters:: # Elasticity parameters E = 10.0 nu = 0.3 mu = E/(2*(1 + nu)) lmbda = E*nu/((1 + nu)*(1 - 2*nu)) # Both the first variation of the potential energy, and the Jacobian of # the variation, can be automatically computed by a call to # ``derivative``::
def sigma(v): return 2.0 * mu * sym(grad(v)) + lmbda * tr(sym(grad(v))) * Identity( len(v))
def S(tau): return tau - ufl.Identity(2) * ufl.tr(tau)
def sigma(v): return 2.0*mu*epsilon(v) + lmbda*ufl.tr(epsilon(v)) \ * ufl.Identity(v.geometric_dimension())
odeint = dolfiny.odeint.ODEInt(t=time, dt=dt, x=m, xt=mt, rho=0.95) # Expression for time-integrated quantities u_expr = u + odeint.integral_dt(v) # Configuration gradient I = ufl.Identity(v.geometric_dimension()) # noqa: E741 F = I + ufl.grad( u_expr) # deformation gradient as function of time-integrated velocity dotF = ufl.grad(v) # rate of deformation gradient as function of velocity # Strain measures # dot E = dot E(u,v) total strain rate dotE = 1 / 2 * (dotF.T * F + F.T * dotF) # dot E = dot E(S) elastic strain rate dotEs = 1 / (2 * mu) * St - la / (2 * mu * (3 * la + 2 * mu)) * ufl.tr(St) * I # Variation of rate of Green-Lagrange strain δdotE = dolfiny.expression.derivative(dotE, m, δm) # Weak form (as one-form) f = ufl.inner(δv, rho * vt) * dx + ufl.inner(δv, eta * v) * dx \ + ufl.inner(δdotE, S) * dx + ufl.inner(δS, dotEs - dotE) * dx \ - ufl.inner(δv, rho * b) * dx # Optional: linearise weak form # f = dolfiny.expression.linearise(dolfiny.expression.evaluate(f, u_expr, u), m, [v, S, u]) # linearise # Overall form (as one-form) F = odeint.discretise_in_time(f) # Overall form (as list of forms)
def inv(A): """Matrix invariants""" return ufl.tr(A), 1. / 2 * A[_i, _j] * A[_i, _j], ufl.det(A)
rho = Constant(cell) K = Constant(cell) c00 = Constant(cell) c11 = Constant(cell) c22 = Constant(cell) # Deformation gradient I = Identity(d) F = I + grad(u) F = variable(F) Finv = inv(F) J = det(F) # Left Cauchy-Green deformation tensor B = F * F.T I1_B = tr(B) I2_B = (I1_B**2 - tr(B * B)) / 2 I3_B = J**2 # Right Cauchy-Green deformation tensor C = F.T * F I1_C = tr(C) I2_C = (I1_C**2 - tr(C * C)) / 2 I3_C = J**2 # Green strain tensor E = (C - I) / 2 # Mapping of strain in fiber directions Ef = A * E * A.T