def set_boundaries(self): self.boundaries = fe.MeshFunction("size_t", self.mesh, self.mesh.topology().dim() - 1) self.boundaries.set_all(0) self.ds = fe.Measure("ds")(subdomain_data=self.boundaries) self.I = fe.Identity(self.mesh.topology().dim()) self.normal = fe.FacetNormal(self.mesh)
def largeKinematics(u): d = u.geometric_dimension() I = fe.Identity(d) Fgrad = I + fe.grad(u) C = Fgrad.T * Fgrad E = 0.5 * (C - I) return E
def __setup_a_priori(self): """Sets up the attributes and petsc solver for the a priori quality check. Returns ------- None """ options = [[['ksp_type', 'preonly'], ['pc_type', 'jacobi'], ['pc_jacobi_type', 'diagonal'], ['ksp_rtol', 1e-16], ['ksp_atol', 1e-20], ['ksp_max_it', 1000]]] self.ksp_prior = PETSc.KSP().create() _setup_petsc_options([self.ksp_prior], options) self.transformation_container = fenics.Function( self.form_handler.deformation_space) dim = self.mesh.geometric_dimension() if not self.volume_change > 1: raise ConfigError('MeshQuality', 'volume_change', 'This parameter has to be larger than 1.') self.a_prior = self.trial_dg0 * self.test_dg0 * self.dx self.L_prior = fenics.det( fenics.Identity(dim) + fenics.grad(self.transformation_container) ) * self.test_dg0 * self.dx
def Kinematics(u): # Kinematics d = u.geometric_dimension() I = fe.Identity(d) # Identity tensor F = I + fe.grad(u) # Deformation gradient F = fe.variable(F) # To differentiate Psi(F) J = fe.det(F) # Jacobian of F C = F.T*F # Right Cauchy-Green deformation tensor Ic = fe.tr(C) # Trace of C return [F, J, C, Ic]
def compute_shape_derivative(self): """Computes the part of the shape derivative that comes from the regularization Returns ------- ufl.form.Form The weak form of the shape derivative coming from the regularization """ V = self.form_handler.test_vector_field if self.has_regularization: n = fenics.FacetNormal(self.form_handler.mesh) I = fenics.Identity(self.form_handler.mesh.geometric_dimension()) self.shape_form = Constant(self.mu_surface) * ( self.current_surface - Constant(self.target_surface)) * t_div( V, n) * self.ds if not self.measure_hole: self.shape_form += Constant(self.mu_volume) * ( self.current_volume - Constant(self.target_volume)) * div(V) * self.dx self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_x - Constant(self.target_barycenter_list[0]))\ *(self.current_barycenter_x/self.current_volume*div(V) + 1/self.current_volume*(V[0] + self.spatial_coordinate[0]*div(V)))*self.dx \ + Constant(self.mu_barycenter)*(self.current_barycenter_y - Constant(self.target_barycenter_list[1]))\ *(self.current_barycenter_y/self.current_volume*div(V) + 1/self.current_volume*(V[1] + self.spatial_coordinate[1]*div(V)))*self.dx if self.form_handler.mesh.geometric_dimension() == 3: self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_z - Constant(self.target_barycenter_list[2]))\ *(self.current_barycenter_z/self.current_volume*div(V) + 1/self.current_volume*(V[2] + self.spatial_coordinate[2]*div(V)))*self.dx else: self.shape_form -= Constant(self.mu_volume) * ( self.current_volume - Constant(self.target_volume)) * div(V) * self.dx self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_x - Constant(self.target_barycenter_list[0]))\ *(self.current_barycenter_x/self.current_volume*div(V) - 1/self.current_volume*(V[0] + self.spatial_coordinate[0]*div(V)))*self.dx \ + Constant(self.mu_barycenter)*(self.current_barycenter_y - Constant(self.target_barycenter_list[1]))\ *(self.current_barycenter_y/self.current_volume*div(V) - 1/self.current_volume*(V[1] + self.spatial_coordinate[1]*div(V)))*self.dx if self.form_handler.mesh.geometric_dimension() == 3: self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_z - Constant(self.target_barycenter_list[2]))\ *(self.current_barycenter_z/self.current_volume*div(V) - 1/self.current_volume*(V[2] + self.spatial_coordinate[2]*div(V)))*self.dx return self.shape_form else: dim = self.form_handler.mesh.geometric_dimension() return inner(fenics.Constant([0] * dim), V) * self.dx
def sigma(u): return lambda_ * fs.nabla_grad(u) * fs.Identity(d) + 2 * mu * epsilon(u)
v = fs.TestFunction(V) f = fs.Constant((0, 0, -rho * g)) T = fs.Constant((0, 0, 0)) a = fs.inner(sigma(u), epsilon(v)) * fs.dx L = fs.dot(f, v) * fs.dx + fs.dot(T, v) * fs.ds # Compute solution u = fs.Function(V) fs.solve(a == L, u, bc) # Plot solution plt.figure() fs.plot(u, title='Displacement', mode='displacement') # Plot stress s = sigma(u) - (1. / 3) * fs.tr(sigma(u)) * fs.Identity(d) # deviatoric stress von_Mises = fs.sqrt(3. / 2 * fs.inner(s, s)) V = fs.FunctionSpace(mesh, 'P', 1) von_Mises = fs.project(von_Mises, V) plt.figure() fs.plot(von_Mises, title='Stress intensity') # Compute magnitude of displacement u_magnitude = fs.sqrt(fs.dot(u, u)) u_magnitude = fs.project(u_magnitude, V) plt.figure() fs.plot(u_magnitude, title='Displacement magnitude') print('min/max u:', u_magnitude.vector().min(), u_magnitude.vector().max()) # Save solution to file in VTK format fs.File('results/displacement.pvd') << u
def D(E, sigma): # this is important: it's where the coupling occurs return fn.Identity(2) * D0 * (1 + D0*E) + pow(D0, 2) * sigma
# time loop iteration = 0 while t <= TFinal: # log time print "t =", t # solve poroelasticity fn.solve(pe_left_hand_side == pe_right_hand_side, pe_solution, boundary_conditions) # split u, phi, p = pe_solution.split() # calculate sigma and z sigma = fn.project(2 * mu * strain(u_old) - phi * fn.Identity(2) + \ n_old * c_1 * fn.outer(f_0, f_0), Whf) z = fn.project((u - u_old)/dt, Vhf) rd_rhs_2 = rd_right_hand_side + Istim(t) * F * fn.dx # solve reaction-diffusion fn.solve(rd_left_hand_side == rd_rhs_2, rd_solution) # split E, n = rd_solution.split() # save if divisible by frequency if iteration % frequency == 0: u.rename("u", "u")
def def_grad(u): I = fe.Identity(u.ufl_shape[0]) return I + fe.grad(u)
def sigma_d(u): return eta_b * ufl.nabla_div(u) * fenics.Identity( 2) + 2 * eta_s * epsilon(u)
def get_new_value(self, r: fenics.Function) -> fenics.Expression: return 2.0*self.lame_mu*fenics.sym(fenics.grad(r)) \ + self.lame_lambda*fenics.tr(fenics.sym(fenics.grad(r)))*fenics.Identity(len(r))
def sigma(r): return 2.0 * mu * fnc.sym(fnc.grad(r)) + lmbda * fnc.tr( fnc.sym(fnc.grad(r))) * fnc.Identity(len(r))
def deformation_grad(u): I = fe.Identity(dim) return fe.variable(I + grad(u))
def Identity(self, dim): return FEN.Identity(dim)
def DeformationGradient(self, u): I = fa.Identity(u.geometric_dimension()) return I + fa.grad(u)
def sigma_a(u, rho): return -zeta_1 * rho / ( 1 + zeta_2 * rho) * mu_a * fenics.Identity(2) * (K_0)
def sigma(u, p): return 2*mu*epsilon(u) - p*fs.Identity(len(u))
uViewer << u # Maximum and minimum displacement u_magnitude = fe.sqrt(fe.dot(u, u)) u_magnitude = fe.project(u_magnitude, W) print('Min/Max displacement:', u_magnitude.vector().array().min(), u_magnitude.vector().array().max()) # Computation of the large deformation strains #cprint("Computing the deformation tensor and saving to file...", 'green') epsilon_u = largeKinematics(u) epsilon_u_project = fe.project(epsilon_u, Z) epsilonViewer = fe.File('paraview/strain.pvd') epsilonViewer << epsilon_u_project # Computation of the stresses #cprint("Stress derivation and saving to file...", 'green') S = fe.diff(psi, E) S_project = fe.project(S, Z) sigmaViewer = fe.File('paraview/stress.pvd') sigmaViewer << S_project # Computation of an equivalent stress s = S - (1. / 3) * fe.tr(S) * fe.Identity(u.geometric_dimension()) von_Mises = fe.sqrt(3. / 2 * fe.inner(s, s)) von_Mises_project = fe.project(von_Mises, W) misesViewer = fe.File('paraview/mises.pvd') misesViewer << von_Mises_project print("Maximum equivalent stress:", von_Mises_project.vector().array().max())
def solve(self): """ Solve the Stokes_sim problem based on the mathematical procedure presented by Ximo in this thesis. Returns: """ # -------------------------------------------------------------------- # DEFINE THE INPUTS # # -------------------------------------------------------------------- self.get_mesh() self.get_boundaries() self.get_subdomains() self.get_restrictions() # Create a block of restrictions. """ This variable will be used by multiphenics when creating function spaces. It will create function spaces on the introduced restrictions. """ block_restrictions = [ self.restrictions_dict['liquid_rtc'], self.restrictions_dict['liquid_rtc'], self.restrictions_dict['interface_rtc'] ] # -------------------------------------------------------------------- # -------------------------------------------------------------------- # FUNCTION SPACES # # -------------------------------------------------------------------- V = fn.VectorFunctionSpace(self.mesh, "CG", 2) Q = fn.FunctionSpace(self.mesh, "CG", 1) L = fn.FunctionSpace(self.mesh, "DGT", 0) # DGT 0. # Create a block function space. """ Block Function Spaces are similar to FEniCS function spaces. However, since we are creating function spaces based on the block of restrictions, we need to create a 'block of function spaces' for each of the restrictions. That block of functions is the list [V, Q, L] from the line of code below this comment. They are assigned in the same order in which the block of restrictions has been created, that is: - V -> liquid_rtc - Q -> liquid_rtc - L -> interface_rtc """ W = mp.BlockFunctionSpace([V, Q, L], restrict=block_restrictions) # -------------------------------------------------------------------- # -------------------------------------------------------------------- # TRIAL/TEST FUNCTIONS # # -------------------------------------------------------------------- """ Trial and test functions are created the multiphenics commands for creating these functions. However, the difference wrt the FEniCS functions for this purpose, a trial/test function will be created for each of the restrictions (for each function space of the BlockFunctionSpace). """ test = mp.BlockTestFunction(W) (v, q, l) = mp.block_split(test) trial = mp.BlockTrialFunction(W) (u, p, theta) = mp.block_split(trial) # Use a value of previous velocity to make the system linear, as explained by Ximo. u_prev = fn.Function(V) u_prev.assign(fn.Constant((0.1, 0.1))) # -------------------------------------------------------------------- # -------------------------------------------------------------------- # MEASURES # # -------------------------------------------------------------------- self.get_measures() self.dS = self.dS( self.boundaries_ids['Interface']) # Restrict to the interface. # Check proper marking of the interface. assert fn.assemble( 1 * self.dS(domain=self.mesh) ) > 0., "The length of the interface is zero, wrong marking." # -------------------------------------------------------------------- # DEFINE THE VARIATIONAL PROBLEM # # -------------------------------------------------------------------- r = fn.SpatialCoordinate(self.mesh)[0] n = fn.FacetNormal(self.mesh) tan_vector = fn.as_vector((n[1], -n[0])) e_r = fn.Constant((1., 0.)) # Define unit radial vector e_z = fn.Constant((0., 1.)) # Define unit axial vector aux_term = (self.eps_r * self.Ca * np.sqrt(self.B)) / (1 + self.Lambda * (self.T_h - 1)) # Define the term a. a = r * aux_term * fn.inner((fn.grad(u) + fn.grad(u).T), (fn.grad(v) + fn.grad(v).T)) * self.dx( self.subdomains_ids['Liquid']) a += 2 / r * aux_term * fn.dot(u, e_r) * fn.dot(v, e_r) * self.dx( self.subdomains_ids['Liquid']) # Define the term d. del_operation = fn.dot(fn.grad(u), u_prev) d = r * self.eps_r**2 * self.We * fn.dot(del_operation, v) * self.dx( self.subdomains_ids['Liquid']) # Define the term l1. def evaporated_charge(): return (self.sigma * self.T_h) / (self.eps_r * self.Chi) * fn.exp( -self.Phi / self.T_h * (1 - self.B**0.25 * fn.sqrt(self.E_v_n))) l1 = -r * evaporated_charge() * l("+") * self.dS # Define the term l2. l2 = r * self.sigma * fn.dot(self.E_v, tan_vector("-")) * fn.dot( v("+"), tan_vector("-")) * self.dS # Define the term b. def b(vector, scalar): radial_term = r * fn.dot(vector, e_r) axial_term = r * fn.dot(vector, e_z) return -(radial_term.dx(0) + axial_term.dx(1)) * scalar * self.dx( self.subdomains_ids['Liquid']) # Define the term c. c1 = -r * fn.dot(v("+"), n("-")) * theta("+") * self.dS c2 = -r * fn.dot(u("+"), n("-")) * l("+") * self.dS # Define the tensors to be solved. # The following order is used. # u p theta # aa = [ [a + d, b(v, p), c1], # Test function v [b(u, q), 0, 0], # Test function q [c2, 0, 0] ] # Test function l bb = [l2, fn.Constant(0.) * q("+") * self.dS, l1] # -------------------------------------------------------------------- # DEFINE THE BOUNDARY CONDITIONS # # -------------------------------------------------------------------- """ When creating Dirichlet boundary conditions with the multiphenics code, a function space from the Block must be selected, depending on which subdomain/boundary should it be applied. To do so, the .sub method is used. The input is an integer, which depends on the function space in which you want the BC to be applied. For this case, inputs of 0, 1 and 2 are accepted, because we have 3 restrictions. The assignments of these ids to the function space is the one done in the block of restrictions. """ bcs_u = [] bcs_p = [] for i in self.boundary_conditions: if 'Dirichlet' in self.boundary_conditions[i]: bc_val = self.boundary_conditions[i]['Dirichlet'][1] if self.boundary_conditions[i]['Dirichlet'][0] == 'v': bc = mp.DirichletBC(W.sub(0), bc_val, self.boundaries, self.boundaries_ids[i]) # Check the created boundary condition. assert len(bc.get_boundary_values() ) > 0., f'Wrongly defined boundary {i}' bcs_u.append(bc) elif self.boundary_conditions[i]['Dirichlet'][0] == 'p': bc = mp.DirichletBC(W.sub(1), bc_val, self.boundaries, self.boundaries_ids[i]) # Check the created boundary condition. assert len(bc.get_boundary_values() ) > 0., f'Wrongly defined boundary {i}' bcs_p.append(bc) bcs_block = mp.BlockDirichletBC([bcs_u, bcs_p]) # -------------------------------------------------------------------- # -------------------------------------------------------------------- # SOLVE # # -------------------------------------------------------------------- # Assemble the system. AA = mp.block_assemble(aa) BB = mp.block_assemble(bb) # Apply the boundary conditions. bcs_block.apply(AA) bcs_block.apply(BB) # Solve. uptheta = mp.BlockFunction(W) mp.block_solve(AA, uptheta.block_vector(), BB) (u, p, theta) = uptheta.block_split() self.u = u self.p_star = p self.theta = theta # Compute normal and tangential velocity components. u_n = fn.dot(u, n) self.u_n = Stokes.block_project( u_n, self.mesh, self.restrictions_dict['interface_rtc'], self.boundaries, self.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal', sign='-') u_t = fn.dot(u, tan_vector) self.u_t = Stokes.block_project( u_t, self.mesh, self.restrictions_dict['interface_rtc'], self.boundaries, self.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal', sign='+') # Compute the convection charge transport. special = (fn.Identity(self.mesh.topology().dim()) - fn.outer(n, n)) * fn.grad(self.sigma) self.j_conv = self.Kc * self.B**( 3 / 2) * (fn.dot(self.sigma * n, fn.dot(fn.grad(self.u), n)) - fn.dot(self.u, special)) self.j_conv = Stokes.block_project( self.j_conv, self.mesh, self.restrictions_dict['interface_rtc'], self.boundaries, self.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal', sign='-')
def sigma_e(u): return lambda_ * ufl.nabla_div(u) * fenics.Identity( 2) + 2 * mu * epsilon(u)
def compute_static_deformation(self): assert self.mesh is not None # now we define subdomains on the mesh bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary') top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary') # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7') # Initialize mesh function for interior domains self.domains = fe.MeshFunction('size_t', self.mesh, 3) self.domains.set_all(0) # middle.mark(self.domains, 1) # Initialize mesh function for boundary domains self.boundaries = fe.MeshFunction('size_t', self.mesh, 2) self.boundaries.set_all(0) bottom.mark(self.boundaries, 1) top.mark(self.boundaries, 2) # Define new measures associated with the interior domains and # exterior boundaries self.dx = fe.Measure('dx', domain=self.mesh, subdomain_data=self.domains) self.ds = fe.Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) # define function spaces V = fe.VectorFunctionSpace(self.mesh, "Lagrange", 1) # now we define subdomains on the mesh bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary') top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary') # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7') d = self.mesh.geometry().dim() # Initialize mesh function for interior domains self.domains = fe.MeshFunction('size_t', self.mesh, d) self.domains.set_all(0) # middle.mark(self.domains, 1) # Initialize mesh function for boundary domains self.boundaries = fe.MeshFunction('size_t', self.mesh, d - 1) self.boundaries.set_all(0) bottom.mark(self.boundaries, 1) top.mark(self.boundaries, 2) # Define new measures associated with the interior domains and # exterior boundaries self.dx = fe.Measure('dx', domain=self.mesh, subdomain_data=self.domains) self.ds = fe.Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) c_zero = fe.Constant((0, 0, 0)) # define boundary conditions bc_bottom = fe.DirichletBC(V, c_zero, bottom) bc_top = fe.DirichletBC(V, c_zero, top) bcs = [bc_bottom] # , bc_top] # define functions du = TrialFunction(V) v = TestFunction(V) u = Function(V) B = fe.Constant((0., 2.0, 0.)) T = fe.Constant((0.0, 0.0, 0.0)) d = u.geometric_dimension() I = fe.Identity(d) F = I + grad(u) C = F.T * F I_1 = tr(C) J = det(F) E, mu = 10., 0.3 mu, lmbda = fe.Constant(E / (2 * (1 + mu))), fe.Constant( E * mu / ((1 + mu) * (1 - 2 * mu))) # stored energy (comp. neo-hookean model) psi = (mu / 2.) * (I_1 - 3) - mu * fe.ln(J) + (lmbda / 2.) * (fe.ln(J))**2 dx = self.dx ds = self.ds Pi = psi * fe.dx - dot(B, u) * fe.dx - dot(T, u) * fe.ds F = fe.derivative(Pi, u, v) J = fe.derivative(F, u, du) fe.solve(F == 0, u, bcs, J=J) # save results self.u = u # write to disk output = fe.File("/tmp/static.pvd") output << u
def sigma(u): return (lmbda * fn.tr(eps(u)) * fn.Identity(2) + 2 * mu * eps(u))
u_right = fe.Expression(("0.0", "0.0", "0.0"), element=element_3) p_left = fe.Constant(0.) # Define acting force b = fe.Constant((0.0, 0.0, 0.0)) # Body force per unit volume t_bar = fe.Constant((0.0, 0.0, 0.0)) # Traction force on the boundary # Define test and trial functions w = fe.Function(V) # most recently computed solution (u, p) = fe.split(w) (v, q) = fe.TestFunctions(V) dw = fe.TrialFunction(V) # Kinematics d = u.geometric_dimension() I = fe.Identity(d) # Identity tensor F = fe.variable(I + grad(u)) # Deformation gradient C = fe.variable(F.T * F) # Right Cauchy-Green tensor J = fe.det(C) DE = lambda v: 0.5 * (F.T * grad(v) + grad(v).T * F) a_0 = fe.as_vector([[1.0], [0.], [0.]]) # Invariants I_1 = tr(C) I_2 = 0.5 * (tr(C)**2 - tr(C * C)) I_4 = dot(a_0.T, C * a_0)
def identity(u): return fe.Identity(u.ufl_shape[0])