def S(self, u_, p_, ivar=None, tang=False): C_ = variable(self.kin.C(u_)) stress = constantvalue.zero((3, 3)) # volumetric (kinematic) growth if self.mat_growth: theta_ = ivar["theta"] # material has to be evaluated with C_e only, however total S has # to be computed by differentiating w.r.t. C (S = 2*dPsi/dC) self.mat = materiallaw(self.C_e(C_, theta_), self.I) else: self.mat = materiallaw(C_, self.I) m = 0 for matlaw in self.matmodels: stress += self.add_stress_mat(matlaw, self.matparams[m], ivar, C_) m += 1 # add remodeled material if self.mat_growth and self.mat_remodel: self.stress_base = stress self.stress_remod = constantvalue.zero((3, 3)) m = 0 for matlaw in self.matmodels_remod: self.stress_remod += self.add_stress_mat( matlaw, self.matparams_remod[m], ivar, C_) m += 1 # update the stress expression: S = (1-phi(theta)) * S_base + phi(theta) * S_remod stress = ( 1. - self.phi_remod(theta_) ) * self.stress_base + self.phi_remod(theta_) * self.stress_remod # if we have p (hydr. pressure) as variable in a 2-field functional if self.incompr_2field: if self.mat_growth: # TeX: S_{\mathrm{vol}} = -2 \frac{\partial[p(J^{\mathrm{e}}-1)]}{\partial \boldsymbol{C}} stress += -2. * diff( p_ * (sqrt(det(self.C_e(C_, theta_))) - 1.), C_) else: # TeX: S_{\mathrm{vol}} = -2 \frac{\partial[p(J-1)]}{\partial \boldsymbol{C}} = -Jp\boldsymbol{C}^{-1} stress += -2. * diff(p_ * (sqrt(det(C_)) - 1.), C_) if tang: return 2. * diff(stress, C_) else: return stress
def stress(self, strain, alpha): # Differentiate the elastic energy w.r.t. the strain tensor eps_ = ufl.variable(strain) # Derivative of energy w.r.t. the strain tensor to obtain the stress # tensor sigma = ufl.diff(self.elastic_energy_density_strain(eps_, alpha), eps_) return sigma
def hyperelasticity(domain, q, p, nf=0): # Based on https://github.com/firedrakeproject/firedrake-bench/blob/experiments/forms/firedrake_forms.py V = ufl.FunctionSpace(domain, ufl.VectorElement('P', domain.ufl_cell(), q)) P = ufl.FunctionSpace(domain, ufl.VectorElement('P', domain.ufl_cell(), p)) v = ufl.TestFunction(V) du = ufl.TrialFunction(V) # Incremental displacement u = ufl.Coefficient(V) # Displacement from previous iteration B = ufl.Coefficient(V) # Body force per unit mass # Kinematics I = ufl.Identity(domain.ufl_cell().topological_dimension()) F = I + ufl.grad(u) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor E = (C - I)/2 # Euler-Lagrange strain tensor E = ufl.variable(E) # Material constants mu = ufl.Constant(domain) # Lame's constants lmbda = ufl.Constant(domain) # Strain energy function (material model) psi = lmbda/2*(ufl.tr(E)**2) + mu*ufl.tr(E*E) S = ufl.diff(psi, E) # Second Piola-Kirchhoff stress tensor PK = F*S # First Piola-Kirchoff stress tensor # Variational problem it = ufl.inner(PK, ufl.grad(v)) - ufl.inner(B, v) f = [ufl.Coefficient(P) for _ in range(nf)] return ufl.derivative(reduce(ufl.inner, list(map(ufl.div, f)) + [it])*ufl.dx, u, du)
def s(e): """ Stress as function of strain from strain energy function """ e = ufl.variable(e) W = lamé_μ * e * e + lamé_λ / 2 * e**2 # Saint-Venant Kirchhoff s = ufl.diff(W, e) return s
def S(E): """ Stress as function of strain from strain energy function """ E = ufl.variable(E) W = lamé_μ * ufl.inner(E, E) + lamé_λ / 2 * ufl.tr(E)**2 # Saint-Venant Kirchhoff S = ufl.diff(W, E) return S
def eigenstate_legacy(A): """Eigenvalues and eigenprojectors of the 3x3 (real-valued) tensor A. Provides the spectral decomposition A = sum_{a=0}^{2} λ_a * E_a with eigenvalues λ_a and their associated eigenprojectors E_a = n_a^R x n_a^L ordered by magnitude. The eigenprojectors of eigenvalues with multiplicity n are returned as 1/n-fold projector. Note: Tensor A must not have complex eigenvalues! """ if ufl.shape(A) != (3, 3): raise RuntimeError( f"Tensor A of shape {ufl.shape(A)} != (3, 3) is not supported!") # eps = 1.0e-10 # A = ufl.variable(A) # # --- determine eigenvalues λ0, λ1, λ2 # # additively decompose: A = tr(A) / 3 * I + dev(A) = q * I + B q = ufl.tr(A) / 3 B = A - q * ufl.Identity(3) # observe: det(λI - A) = 0 with shift λ = q + ω --> det(ωI - B) = 0 = ω**3 - j * ω - b j = ufl.tr( B * B ) / 2 # == -I2(B) for trace-free B, j < 0 indicates A has complex eigenvalues b = ufl.tr(B * B * B) / 3 # == I3(B) for trace-free B # solve: 0 = ω**3 - j * ω - b by substitution ω = p * cos(phi) # 0 = p**3 * cos**3(phi) - j * p * cos(phi) - b | * 4 / p**3 # 0 = 4 * cos**3(phi) - 3 * cos(phi) - 4 * b / p**3 | --> p := sqrt(j * 4 / 3) # 0 = cos(3 * phi) - 4 * b / p**3 # 0 = cos(3 * phi) - r with -1 <= r <= +1 # phi_k = [acos(r) + (k + 1) * 2 * pi] / 3 for k = 0, 1, 2 p = 2 / ufl.sqrt(3) * ufl.sqrt(j + eps**2) # eps: MMM r = 4 * b / p**3 r = ufl.Max(ufl.Min(r, +1 - eps), -1 + eps) # eps: LMM, MMH phi = ufl.acos(r) / 3 # sorted eigenvalues: λ0 <= λ1 <= λ2 λ0 = q + p * ufl.cos(phi + 2 / 3 * ufl.pi) # low λ1 = q + p * ufl.cos(phi + 4 / 3 * ufl.pi) # middle λ2 = q + p * ufl.cos(phi) # high # # --- determine eigenprojectors E0, E1, E2 # E0 = ufl.diff(λ0, A).T E1 = ufl.diff(λ1, A).T E2 = ufl.diff(λ2, A).T # return [λ0, λ1, λ2], [E0, E1, E2]
def eigenstate(A): """Eigenvalues and eigenprojectors of the 3x3 (real-valued) tensor A. Provides the spectral decomposition A = sum_{a=0}^{2} λ_a * E_a with (ordered) eigenvalues λ_a and their associated eigenprojectors E_a = n_a^R x n_a^L. Note: Tensor A must not have complex eigenvalues! """ if ufl.shape(A) != (3, 3): raise RuntimeError( f"Tensor A of shape {ufl.shape(A)} != (3, 3) is not supported!") # eps = 3.0e-16 # slightly above 2**-(53 - 1), see https://en.wikipedia.org/wiki/IEEE_754 # A = ufl.variable(A) # # --- determine eigenvalues λ0, λ1, λ2 # I1, I2, I3 = invariants_principal(A) dq = 2 * I1**3 - 9 * I1 * I2 + 27 * I3 # Δx = [ A[0, 1] * A[1, 2] * A[2, 0] - A[0, 2] * A[1, 0] * A[2, 1], A[0, 1]**2 * A[1, 2] - A[0, 1] * A[0, 2] * A[1, 1] + A[0, 1] * A[0, 2] * A[2, 2] - A[0, 2]**2 * A[2, 1], A[0, 0] * A[0, 1] * A[2, 1] - A[0, 1]**2 * A[2, 0] - A[0, 1] * A[2, 1] * A[2, 2] + A[0, 2] * A[2, 1]**2, A[0, 0] * A[0, 2] * A[1, 2] + A[0, 1] * A[1, 2]**2 - A[0, 2]**2 * A[1, 0] - A[0, 2] * A[1, 1] * A[1, 2], A[0, 0] * A[0, 1] * A[1, 2] - A[0, 1] * A[0, 2] * A[1, 0] - A[0, 1] * A[1, 2] * A[2, 2] + A[0, 2] * A[1, 2] * A[2, 1], # noqa: E501 A[0, 0] * A[0, 2] * A[2, 1] - A[0, 1] * A[0, 2] * A[2, 0] + A[0, 1] * A[1, 2] * A[2, 1] - A[0, 2] * A[1, 1] * A[2, 1], # noqa: E501 A[0, 1] * A[1, 0] * A[1, 2] - A[0, 2] * A[1, 0] * A[1, 1] + A[0, 2] * A[1, 0] * A[2, 2] - A[0, 2] * A[1, 2] * A[2, 0], # noqa: E501 A[0, 0]**2 * A[1, 2] - A[0, 0] * A[0, 2] * A[1, 0] - A[0, 0] * A[1, 1] * A[1, 2] - A[0, 0] * A[1, 2] * A[2, 2] + A[0, 1] * A[1, 0] * A[1, 2] + A[0, 2] * A[1, 0] * A[2, 2] + A[1, 1] * A[1, 2] * A[2, 2] - A[1, 2]**2 * A[2, 1], # noqa: E501 A[0, 0]**2 * A[1, 2] - A[0, 0] * A[0, 2] * A[1, 0] - A[0, 0] * A[1, 1] * A[1, 2] - A[0, 0] * A[1, 2] * A[2, 2] + A[0, 2] * A[1, 0] * A[1, 1] + A[0, 2] * A[1, 2] * A[2, 0] + A[1, 1] * A[1, 2] * A[2, 2] - A[1, 2]**2 * A[2, 1], # noqa: E501 A[0, 0] * A[0, 1] * A[1, 1] - A[0, 0] * A[0, 1] * A[2, 2] - A[0, 1]**2 * A[1, 0] + A[0, 1] * A[0, 2] * A[2, 0] - A[0, 1] * A[1, 1] * A[2, 2] + A[0, 1] * A[2, 2]**2 + A[0, 2] * A[1, 1] * A[2, 1] - A[0, 2] * A[2, 1] * A[2, 2], # noqa: E501 A[0, 0] * A[0, 1] * A[1, 1] - A[0, 0] * A[0, 1] * A[2, 2] + A[0, 0] * A[0, 2] * A[2, 1] - A[0, 1]**2 * A[1, 0] - A[0, 1] * A[1, 1] * A[2, 2] + A[0, 1] * A[1, 2] * A[2, 1] + A[0, 1] * A[2, 2]**2 - A[0, 2] * A[2, 1] * A[2, 2], # noqa: E501 A[0, 0] * A[0, 1] * A[1, 2] - A[0, 0] * A[0, 2] * A[1, 1] + A[0, 0] * A[0, 2] * A[2, 2] - A[0, 1] * A[1, 1] * A[1, 2] - A[0, 2]**2 * A[2, 0] + A[0, 2] * A[1, 1]**2 - A[0, 2] * A[1, 1] * A[2, 2] + A[0, 2] * A[1, 2] * A[2, 1], # noqa: E501 A[0, 0] * A[0, 2] * A[1, 1] - A[0, 0] * A[0, 2] * A[2, 2] - A[0, 1] * A[0, 2] * A[1, 0] + A[0, 1] * A[1, 1] * A[1, 2] - A[0, 1] * A[1, 2] * A[2, 2] + A[0, 2]**2 * A[2, 0] - A[0, 2] * A[1, 1]**2 + A[0, 2] * A[1, 1] * A[2, 2], # noqa: E501 A[0, 0]**2 * A[1, 1] - A[0, 0]**2 * A[2, 2] - A[0, 0] * A[0, 1] * A[1, 0] + A[0, 0] * A[0, 2] * A[2, 0] - A[0, 0] * A[1, 1]**2 + A[0, 0] * A[2, 2]**2 + A[0, 1] * A[1, 0] * A[1, 1] - A[0, 2] * A[2, 0] * A[2, 2] + A[1, 1]**2 * A[2, 2] - A[1, 1] * A[1, 2] * A[2, 1] - A[1, 1] * A[2, 2]**2 + A[1, 2] * A[2, 1] * A[2, 2] ] # noqa: E501 Δy = [ A[0, 2] * A[1, 0] * A[2, 1] - A[0, 1] * A[1, 2] * A[2, 0], A[1, 0]**2 * A[2, 1] - A[1, 0] * A[1, 1] * A[2, 0] + A[1, 0] * A[2, 0] * A[2, 2] - A[1, 2] * A[2, 0]**2, A[0, 0] * A[1, 0] * A[1, 2] - A[0, 2] * A[1, 0]**2 - A[1, 0] * A[1, 2] * A[2, 2] + A[1, 2]**2 * A[2, 0], A[0, 0] * A[2, 0] * A[2, 1] - A[0, 1] * A[2, 0]**2 + A[1, 0] * A[2, 1]**2 - A[1, 1] * A[2, 0] * A[2, 1], A[0, 0] * A[1, 0] * A[2, 1] - A[0, 1] * A[1, 0] * A[2, 0] - A[1, 0] * A[2, 1] * A[2, 2] + A[1, 2] * A[2, 0] * A[2, 1], # noqa: E501 A[0, 0] * A[1, 2] * A[2, 0] - A[0, 2] * A[1, 0] * A[2, 0] + A[1, 0] * A[1, 2] * A[2, 1] - A[1, 1] * A[1, 2] * A[2, 0], # noqa: E501 A[0, 1] * A[1, 0] * A[2, 1] - A[0, 1] * A[1, 1] * A[2, 0] + A[0, 1] * A[2, 0] * A[2, 2] - A[0, 2] * A[2, 0] * A[2, 1], # noqa: E501 A[0, 0]**2 * A[2, 1] - A[0, 0] * A[0, 1] * A[2, 0] - A[0, 0] * A[1, 1] * A[2, 1] - A[0, 0] * A[2, 1] * A[2, 2] + A[0, 1] * A[1, 0] * A[2, 1] + A[0, 1] * A[2, 0] * A[2, 2] + A[1, 1] * A[2, 1] * A[2, 2] - A[1, 2] * A[2, 1]**2, # noqa: E501 A[0, 0]**2 * A[2, 1] - A[0, 0] * A[0, 1] * A[2, 0] - A[0, 0] * A[1, 1] * A[2, 1] - A[0, 0] * A[2, 1] * A[2, 2] + A[0, 1] * A[1, 1] * A[2, 0] + A[0, 2] * A[2, 0] * A[2, 1] + A[1, 1] * A[2, 1] * A[2, 2] - A[1, 2] * A[2, 1]**2, # noqa: E501 A[0, 0] * A[1, 0] * A[1, 1] - A[0, 0] * A[1, 0] * A[2, 2] - A[0, 1] * A[1, 0]**2 + A[0, 2] * A[1, 0] * A[2, 0] - A[1, 0] * A[1, 1] * A[2, 2] + A[1, 0] * A[2, 2]**2 + A[1, 1] * A[1, 2] * A[2, 0] - A[1, 2] * A[2, 0] * A[2, 2], # noqa: E501 A[0, 0] * A[1, 0] * A[1, 1] - A[0, 0] * A[1, 0] * A[2, 2] + A[0, 0] * A[1, 2] * A[2, 0] - A[0, 1] * A[1, 0]**2 - A[1, 0] * A[1, 1] * A[2, 2] + A[1, 0] * A[1, 2] * A[2, 1] + A[1, 0] * A[2, 2]**2 - A[1, 2] * A[2, 0] * A[2, 2], # noqa: E501 A[0, 0] * A[1, 0] * A[2, 1] - A[0, 0] * A[1, 1] * A[2, 0] + A[0, 0] * A[2, 0] * A[2, 2] - A[0, 2] * A[2, 0]**2 - A[1, 0] * A[1, 1] * A[2, 1] + A[1, 1]**2 * A[2, 0] - A[1, 1] * A[2, 0] * A[2, 2] + A[1, 2] * A[2, 0] * A[2, 1], # noqa: E501 A[0, 0] * A[1, 1] * A[2, 0] - A[0, 0] * A[2, 0] * A[2, 2] - A[0, 1] * A[1, 0] * A[2, 0] + A[0, 2] * A[2, 0]**2 + A[1, 0] * A[1, 1] * A[2, 1] - A[1, 0] * A[2, 1] * A[2, 2] - A[1, 1]**2 * A[2, 0] + A[1, 1] * A[2, 0] * A[2, 2], # noqa: E501 A[0, 0]**2 * A[1, 1] - A[0, 0]**2 * A[2, 2] - A[0, 0] * A[0, 1] * A[1, 0] + A[0, 0] * A[0, 2] * A[2, 0] - A[0, 0] * A[1, 1]**2 + A[0, 0] * A[2, 2]**2 + A[0, 1] * A[1, 0] * A[1, 1] - A[0, 2] * A[2, 0] * A[2, 2] + A[1, 1]**2 * A[2, 2] - A[1, 1] * A[1, 2] * A[2, 1] - A[1, 1] * A[2, 2]**2 + A[1, 2] * A[2, 1] * A[2, 2] ] # noqa: E501 Δd = [9, 6, 6, 6, 8, 8, 8, 2, 2, 2, 2, 2, 2, 1] Δ = 0 for i in range(len(Δd)): Δ += Δx[i] * Δd[i] * Δy[i] Δxp = [ A[1, 0], A[2, 0], A[2, 1], -A[0, 0] + A[1, 1], -A[0, 0] + A[2, 2], -A[1, 1] + A[2, 2] ] Δyp = [ A[0, 1], A[0, 2], A[1, 2], -A[0, 0] + A[1, 1], -A[0, 0] + A[2, 2], -A[1, 1] + A[2, 2] ] Δdp = [6, 6, 6, 1, 1, 1] dp = 0 for i in range(len(Δdp)): dp += 1 / 2 * Δxp[i] * Δdp[i] * Δyp[i] # Avoid dp = 0 and disc = 0, both are known with absolute error of ~eps**2 # Required to avoid sqrt(0) derivatives and negative square roots dp += eps**2 Δ += eps**2 phi3 = ufl.atan_2(ufl.sqrt(27) * ufl.sqrt(Δ), dq) # sorted eigenvalues: λ0 <= λ1 <= λ2 λ = [(I1 + 2 * ufl.sqrt(dp) * ufl.cos((phi3 + 2 * ufl.pi * k) / 3)) / 3 for k in range(1, 4)] # # --- determine eigenprojectors E0, E1, E2 # E = [ufl.diff(λk, A).T for λk in λ] return λ, E
def stress(self, eps, alpha): eps_ = variable(eps) sigma = diff(self.elastic_energy_density(eps_, alpha), eps_) return sigma
# unless otherwise specified. # # Next, we will be needing functions for the boundary source ``B``, the # traction ``T`` and the displacement solution itself ``u``:: # 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))
# - # The first line creates an object of type `InitialConditions`. The # following two lines make `u` and `u0` interpolants of `u_init` # (since `u` and `u0` are finite element functions, they may not be # able to represent a given function exactly, but the function can be # approximated by interpolating it in a finite element space). # # ```{index} automatic differentiation # ``` # # The chemical potential $df/dc$ is computed using UFL automatic # differentiation: # Compute the chemical potential df/dc c = ufl.variable(c) f = 100 * c**2 * (1 - c)**2 dfdc = ufl.diff(f, c) # The first line declares that `c` is a variable that some function can # be differentiated with respect to. The next line is the function # $f$ defined in the problem statement, and the third line performs # the differentiation of `f` with respect to the variable `c`. # # It is convenient to introduce an expression for $\mu_{n+\theta}$: # mu_(n+theta) mu_mid = (1.0 - theta) * mu0 + theta * mu # which is then used in the definition of the variational forms:
def dJedC(self, u_, theta_): C_ = variable(self.kin.C(u_)) Je = sqrt(det(self.C_e(C_, theta_))) return diff(Je,C_)
# 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) # Derivative of plastic potential wrt stress dgdS = ufl.diff(g, S)
def assemble_test(cell_batch_size: int): mesh = dolfin.UnitCubeMesh(MPI.comm_world, 40, 40, 40) def isochoric(F): C = F.T*F I_1 = tr(C) I4_f = dot(e_f, C*e_f) I4_s = dot(e_s, C*e_s) I8_fs = dot(e_f, C*e_s) def cutoff(x): return 1.0/(1.0 + ufl.exp(-(x - 1.0)*30.0)) def scaled_exp(a0, a1, argument): return a0/(2.0*a1)*(ufl.exp(b*argument) - 1) E_1 = scaled_exp(a, b, I_1 - 3.) E_f = cutoff(I4_f)*scaled_exp(a_f, b_f, (I4_f - 1.)**2) E_s = cutoff(I4_s)*scaled_exp(a_s, b_s, (I4_s - 1.)**2) E_3 = scaled_exp(a_fs, b_fs, I8_fs**2) E = E_1 + E_f + E_s + E_3 return E cell = mesh.ufl_cell() lamda = dolfin.Constant(0.48, cell) a = dolfin.Constant(1.0, cell) b = dolfin.Constant(1.0, cell) a_s = dolfin.Constant(1.0, cell) b_s = dolfin.Constant(1.0, cell) a_f = dolfin.Constant(1.0, cell) b_f = dolfin.Constant(1.0, cell) a_fs = dolfin.Constant(1.0, cell) b_fs = dolfin.Constant(1.0, cell) # For more fun, make these general vector fields rather than # constants: e_s = dolfin.Constant([0.0, 1.0, 0.0], cell) e_f = dolfin.Constant([1.0, 0.0, 0.0], cell) V = dolfin.FunctionSpace(mesh, ufl.VectorElement("CG", cell, 1)) u = dolfin.Function(V) du = dolfin.function.argument.TrialFunction(V) v = dolfin.function.argument.TestFunction(V) # Misc elasticity related tensors and other quantities F = grad(u) + ufl.Identity(3) F = ufl.variable(F) J = det(F) Fbar = J**(-1.0/3.0)*F # Define energy E_volumetric = lamda*0.5*ln(J)**2 psi = isochoric(Fbar) + E_volumetric # Find first Piola-Kircchoff tensor P = ufl.diff(psi, F) # Define the variational formulation F = inner(P, grad(v))*dx # Take the derivative J = ufl.derivative(F, u, du) a, L = J, F if cell_batch_size > 1: cxx_flags = "-O2 -ftree-vectorize -funroll-loops -march=native -mtune=native" else: cxx_flags = "-O2" assembler = dolfin.fem.assembling.Assembler([[a]], [L], [], form_compiler_parameters={"cell_batch_size": cell_batch_size, "enable_cross_cell_gcc_ext": True, "cpp_optimize_flags": cxx_flags}) t = -time.time() A, b = assembler.assemble( mat_type=dolfin.cpp.fem.Assembler.BlockType.monolithic) t += time.time() return A, b, t
def holzapfel_ogden(mesh, q, p, nf=0): # Based on https://gist.github.com/meg-simula/3ab3fd63264c8cf1912b # # Original credit note: # "Original implementation by Gabriel Balaban, # modified by Marie E. Rognes" from ufl import (Constant, VectorConstant, Identity, Coefficient, TestFunction, conditional, det, diff, dot, exp, grad, inner, tr, variable) # Define some random parameters a = Constant(mesh) b = Constant(mesh) a_s = Constant(mesh) b_s = Constant(mesh) a_f = Constant(mesh) b_f = Constant(mesh) a_fs = Constant(mesh) b_fs = Constant(mesh) # For more fun, make these general vector fields rather than # constants: e_s = VectorConstant(mesh) e_f = VectorConstant(mesh) # Define the isochoric energy contribution def isochoric(C): I_1 = tr(C) I4_f = dot(e_f, C*e_f) I4_s = dot(e_s, C*e_s) I8_fs = dot(e_s, C*e_f) def heaviside(x): return conditional(x < 1, 0, 1) def scaled_exp(a, b, x): return a/(2*b)*(exp(b*x) - 1) E_1 = scaled_exp(a, b, I_1 - 3) E_f = heaviside(I4_f)*scaled_exp(a_f, b_f, (I4_f - 1)**2) E_s = heaviside(I4_s)*scaled_exp(a_s, b_s, (I4_s - 1)**2) E_3 = scaled_exp(a_fs, b_fs, I8_fs**2) E = E_1 + E_f + E_s + E_3 return E # Define mesh and function space V = ufl.FunctionSpace(mesh, ufl.VectorElement("CG", mesh.ufl_cell(), q)) u = Coefficient(V) v = TestFunction(V) # Misc elasticity related tensors and other quantities I = Identity(mesh.ufl_cell().topological_dimension()) F = grad(u) + I F = variable(F) J = det(F) Cbar = J**(-2/3)*F.T*F # Define energy Psi = isochoric(Cbar) # Find first Piola-Kirchhoff tensor P = diff(Psi, F) # Define the variational formulation it = inner(P, grad(v)) P = ufl.FunctionSpace(mesh, ufl.VectorElement('P', mesh.ufl_cell(), p)) f = [ufl.Coefficient(P) for _ in range(nf)] return ufl.derivative(reduce(ufl.inner, list(map(ufl.div, f)) + [it])*ufl.dx, u)
)) svals = np.zeros_like(stretchVals) plt.plot(timeVals, stretchVals) plt.savefig("stretchesVisco.png") plt.close() # stabilization parameters h = FacetArea(mesh) h_avg = avg(h) # new variable name to take derivatives FF = Identity(3) + grad(u) CC = FF.T * FF Fv = variable(FF) S = diff(freeEnergy(Fv.T * Fv, CCv), Fv) # first PK stress dl_interp(CC, C) dl_interp(CC, Cn) my_identity = grad(SpatialCoordinate(mesh)) dl_interp(my_identity, CCv) dl_interp(my_identity, Cvn) dl_interp(my_identity, C_quart) dl_interp(my_identity, C_thr_quart) dl_interp(my_identity, C_half) a_uv = (derivative(freeEnergy(CC, CCv), u, v) * dx + qvals / h_avg * dot(jump(u), jump(v)) * dS) jac = derivative(a_uv, u, du)
def dJdC(self, u_): C_ = variable(self.C(u_)) J = sqrt(det(C_)) return diff(J,C_)
# Create intial conditions and interpolate u.interpolate(u_init) # The first line creates an object of type ``InitialConditions``. The # following two lines make ``u`` and ``u0`` interpolants of ``u_init`` # (since ``u`` and ``u0`` are finite element functions, they may not be # able to represent a given function exactly, but the function can be # approximated by interpolating it in a finite element space). # # .. index:: automatic differentiation # # The chemical potential :math:`df/dc` is computed using automated # differentiation:: # Compute the chemical potential df/dc c = variable(c) f = 100 * c**2 * (1 - c)**2 dfdc = diff(f, c) # The first line declares that ``c`` is a variable that some function can # be differentiated with respect to. The next line is the function # :math:`f` defined in the problem statement, and the third line performs # the differentiation of ``f`` with respect to the variable ``c``. # # It is convenient to introduce an expression for :math:`\mu_{n+\theta}`:: # mu_(n+theta) mu_mid = (1.0 - theta) * mu0 + theta * mu # which is then used in the definition of the variational forms::
# External forces T = Coefficient(u_element) p0 = Coefficient(p_element) # Material parameters FIXME 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