def inverse_map_function_ufl(x, control_points, impact_radii, map_type): if len(control_points) == 0: return x x = fe.variable(x) df, rho = distance_function_segments_ufl(x, control_points, impact_radii) grad_x = fe.diff(df, x) delta_x = fe.conditional( fe.gt(df, rho), fe.Constant((0., 0.)), grad_x * (rho * inverse_ratio_function_ufl(df / rho, map_type) - df)) return delta_x + x
def build_weak_form_staggered(self): self.x_hat = fe.variable(fe.SpatialCoordinate(self.mesh)) self.x = map_function_ufl(self.x_hat, self.control_points, self.impact_radii, self.map_type, self.boundary_info) self.grad_gamma = fe.diff(self.x, self.x_hat) def mfem_grad_wrapper(grad): def mfem_grad(u): return fe.dot(grad(u), fe.inv(self.grad_gamma)) return mfem_grad self.mfem_grad = mfem_grad_wrapper(fe.grad) # A special note (Tianju): We hope to use Model C, but Newton solver fails without the initial guess by Model A if self.i < 2: self.psi_plus = partial(psi_plus_linear_elasticity_model_A, lamda=self.lamda, mu=self.mu) self.psi_minus = partial(psi_minus_linear_elasticity_model_A, lamda=self.lamda, mu=self.mu) else: self.psi_plus = partial(psi_plus_linear_elasticity_model_C, lamda=self.lamda, mu=self.mu) self.psi_minus = partial(psi_minus_linear_elasticity_model_C, lamda=self.lamda, mu=self.mu) print("use model C") self.psi = partial(psi_linear_elasticity, lamda=self.lamda, mu=self.mu) self.sigma_plus = cauchy_stress_plus( strain(self.mfem_grad(self.x_new)), self.psi_plus) self.sigma_minus = cauchy_stress_minus( strain(self.mfem_grad(self.x_new)), self.psi_minus) # self.sigma = cauchy_stress_plus(strain(self.mfem_grad(self.x_new)), self.psi) # self.sigma_degraded = g_d(self.d_new) * self.sigma_plus + self.sigma_minus self.G_u = (g_d(self.d_new) * fe.inner(self.sigma_plus, strain(self.mfem_grad(self.eta))) \ + fe.inner(self.sigma_minus, strain(self.mfem_grad(self.eta)))) * fe.det(self.grad_gamma) * fe.dx if self.solution_scheme == 'explicit': self.G_d = (self.H_old * self.zeta * g_d_prime(self.d_new, g_d) \ + self.G_c / self.l0 * (self.zeta * self.d_new + self.l0**2 * fe.inner(self.mfem_grad(self.zeta), self.mfem_grad(self.d_new)))) * fe.det(self.grad_gamma) * fe.dx else: self.G_d = (history(self.H_old, self.psi_plus(strain(self.mfem_grad(self.x_new))), self.psi_cr) * self.zeta * g_d_prime(self.d_new, g_d) \ + self.G_c / self.l0 * (self.zeta * self.d_new + self.l0**2 * fe.inner(self.mfem_grad(self.zeta), self.mfem_grad(self.d_new)))) * fe.det(self.grad_gamma) * fe.dx
def build_weak_form_staggered(self): self.x_hat = fe.variable(fe.SpatialCoordinate(self.mesh)) self.x = fe.variable( map_function_ufl(self.x_hat, self.control_points, self.impact_radii, self.map_type, self.boundary_info)) self.grad_gamma = fe.diff(self.x, self.x_hat) def mfem_grad_wrapper(grad): def mfem_grad(u): return fe.dot(grad(u), fe.inv(self.grad_gamma)) return mfem_grad self.mfem_grad = mfem_grad_wrapper(fe.grad) self.psi_plus = partial(self.psi_plus_linear_elasticity, lamda=self.lamda, mu=self.mu) self.psi_minus = partial(self.psi_minus_linear_elasticity, lamda=self.lamda, mu=self.mu) self.psi = partial(psi_linear_elasticity, lamda=self.lamda, mu=self.mu) sigma_plus = cauchy_stress_plus(strain(self.mfem_grad(self.x_new)), self.psi_plus) sigma_minus = cauchy_stress_minus(strain(self.mfem_grad(self.x_new)), self.psi_minus) self.u_exact, self.d_exact = self.compute_analytical_solutions_fully_broken( self.x) self.G_u = (g_d(self.d_exact) * fe.inner(sigma_plus, strain(self.mfem_grad(self.eta))) \ + fe.inner(sigma_minus, strain(self.mfem_grad(self.eta)))) * fe.det(self.grad_gamma) * fe.dx self.G_d = (self.d_new - self.d_exact) * self.zeta * fe.det( self.grad_gamma) * fe.dx self.u_initial = self.nonzero_initial_guess(self.x) self.x_new.assign(fe.project(self.u_initial, self.U))
def SolveProblem(LoadCase, ConstitutiveModel, BCsType, FinalRelativeStretch, RelativeStepSize, Dimensions, NumberElements, Mesh, V, u, du, v, Ic, J, F, Psi, Plot = False, Paraview = False): if LoadCase == 'Compression': # Load case [u_0, u_1, InitialState, Direction, Normal, NumberSteps, DeltaStretch] = LoadCaseDefinition(LoadCase, FinalRelativeStretch, RelativeStepSize, Dimensions, BCsType) elif LoadCase == 'Tension': # Load case [u_0, u_1, InitialState, Direction, Normal, NumberSteps, DeltaStretch] = LoadCaseDefinition(LoadCase, FinalRelativeStretch, RelativeStepSize, Dimensions, BCsType) elif LoadCase == 'SimpleShear': # Load case [u_0, u_1, InitialState, Direction, Normal, NumberSteps, DeltaStretch] = LoadCaseDefinition(LoadCase, FinalRelativeStretch*2, RelativeStepSize*2, Dimensions, BCsType) # Boundary conditions [BoundaryConditions, ds] = BCsDefinition(Dimensions, Mesh, V, u_0, u_1, LoadCase, BCsType) # Estimation of the displacement field using Neo-Hookean model (necessary for Ogden) u = Estimate(Ic, J, u, v, du, BoundaryConditions, InitialState, u_1) # Reformulate the problem with the correct constitutive model Pi = Psi * fe.dx # First directional derivative of the potential energy Fpi = fe.derivative(Pi,u,v) # Jacobian of Fpi Jac = fe.derivative(Fpi,u,du) # Define option for the compiler (optional) ffc_options = {"optimize": True, \ "eliminate_zeros": True, \ "precompute_basis_const": True, \ "precompute_ip_const": True } # Define the problem Problem = fe.NonlinearVariationalProblem(Fpi, u, BoundaryConditions, Jac, form_compiler_parameters=ffc_options) # Define the solver Solver = fe.NonlinearVariationalSolver(Problem) # Set solver parameters (optional) Prm = Solver.parameters Prm['nonlinear_solver'] = 'newton' Prm['newton_solver']['linear_solver'] = 'cg' # Conjugate gradient Prm['newton_solver']['preconditioner'] = 'icc' # Incomplete Choleski Prm['newton_solver']['krylov_solver']['nonzero_initial_guess'] = True # Data frame to store values cols = ['Stretches','P'] df = pd.DataFrame(columns=cols, index=range(int(NumberSteps)+1), dtype='float64') if Paraview == True: # Results File Output_Path = os.path.join('OptimizationResults', BCsType, ConstitutiveModel) ResultsFile = xdmffile = fe.XDMFFile(os.path.join(Output_Path, str(NumberElements) + 'Elements_' + LoadCase + '.xdmf')) ResultsFile.parameters["flush_output"] = True ResultsFile.parameters["functions_share_mesh"] = True if Plot == True: plt.rc('figure', figsize=[12,7]) fig = plt.figure() ax = fig.add_subplot(1, 1, 1) # Set the stretch state to initial state StretchState = InitialState # Loop to solve for each step for Step in range(int(NumberSteps+1)): # Update current state u_1.s = StretchState # Compute solution and save displacement Solver.solve() # First Piola Kirchoff (nominal) stress P = fe.diff(Psi, F) # Nominal stress vectors normal to upper surface p = fe.dot(P,Normal) # Reaction force on the upper surface f = fe.assemble(fe.inner(p,Direction)*ds(2)) # Mean nominal stress on the upper surface Pm = f/fe.assemble(1*ds(2)) # Save values to table df.loc[Step].Stretches = StretchState df.loc[Step].P = Pm # Plot if Plot == True: ax.cla() ax.plot(df.Stretches, df.P, color = 'r', linestyle = '--', label = 'P', marker = 'o', markersize = 8, fillstyle='none') ax.set_xlabel('Stretch ratio (-)') ax.set_ylabel('Stresses (kPa)') ax.xaxis.set_major_locator(plt.MultipleLocator(0.02)) ax.legend(loc='upper left', frameon=True, framealpha=1) display(fig) clear_output(wait=True) if Paraview == True: # Project the displacement onto the vector function space u_project = fe.project(u, V, solver_type='cg') u_project.rename('Displacement (mm)', '') ResultsFile.write(u_project,Step) # Compute nominal stress vector p_project = fe.project(p, V) p_project.rename("Nominal stress vector (kPa)","") ResultsFile.write(p_project,Step) # Update the stretch state StretchState += DeltaStretch return df
rho = fe.Constant(1.1e3) kappa = fe.Expression("kappa", kappa=1e8, degree=0) # currently simplifications p_act = fe.Expression("p_act*scale", p_act=1., scale=1., degree=0) lmb_f = fe.Expression("lmd_f", lmd_f=2.0, scale=1., degree=0) I1_ = J**(-2. / 3.) * I_1 I2_ = J**(-4. / 3.) * I_2 W_iso = c_10 * (I1_ - 3) + c_01 * (I2_ - 3) - p * (J - 1) - p**2 / 2 * kappa dx = fe.dx ds = fe.ds P_iso = fe.diff(W_iso, F) S_iso = F * P_iso S_passive = lmb_f**(-1) * p_act * A_00 S = S_iso + S_passive F_static = inner(S, DE(v)) * dx - rho * inner(b, v) * dx - inner( t_bar, v) * ds + (p / kappa + (J - 1)) * q * dx file_u = fe.File("../results/displacement.pvd") file_p = fe.File("../results/pressure.pvd") # Define Dirichlet boundary (x = 0 or x = 1) u_left = fe.Expression(("0.0", "0.0", "0.0"), element=element_3) u_right = fe.Expression(("1.*scale", "0.0", "0.0"),
def map_function_ufl(x_hat, control_points, impact_radii, map_type, boundary_info=None): if len(control_points) == 0: return x_hat x_hat = fe.variable(x_hat) df, rho = distance_function_segments_ufl(x_hat, control_points, impact_radii) grad_x_hat = fe.diff(df, x_hat) delta_x_hat = fe.conditional( fe.gt(df, rho), fe.Constant((0., 0.)), grad_x_hat * (rho * ratio_function_ufl(df / rho, map_type) - df)) if boundary_info is None: return delta_x_hat + x_hat else: last_control_point = control_points[-1] points, directions, rho_default = boundary_info mid_point, mid_point1, mid_point2 = points direct_vec, rotated_vec = directions aux_control_point1 = last_control_point + rho_default * rotated_vec aux_control_point2 = last_control_point - rho_default * rotated_vec w1 = np.linalg.norm(mid_point1 - aux_control_point1) w2 = np.linalg.norm(mid_point2 - aux_control_point2) w0 = np.linalg.norm(mid_point - last_control_point) assert np.absolute(2 * w0 - w1 - w2) < 1e-5 AB = mid_point - last_control_point AP = x_hat - last_control_point x1 = AB[0] y1 = AB[1] x2 = AP[0] y2 = AP[1] mod = fe.sqrt(x1**2 + y1**2) df_to_direct = (x1 * y2 - y1 * x2) / mod # AB x AP df_to_rotated = (x1 * x2 + y1 * y2) / mod k1 = rho_default * (w1 + w2) / (rho_default * (w1 + w2) + df_to_direct * (w1 - w2)) new_df_to_direct = rho_default * ratio_function_ufl( df_to_direct / rho_default, map_type) k2 = rho_default * (w1 + w2) / (rho_default * (w1 + w2) + new_df_to_direct * (w1 - w2)) new_df_to_rotated = df_to_rotated * k1 / k2 x = fe.as_vector(last_control_point + direct_vec * new_df_to_rotated + rotated_vec * new_df_to_direct) return fe.conditional( fe.gt(df_to_rotated, 0), fe.conditional(fe.gt(np.absolute(df_to_direct), rho), x_hat, x), delta_x_hat + x_hat)
def g_d_prime(d, degrad_func): d = fe.variable(d) degrad = degrad_func(d) degrad_prime = fe.diff(degrad, d) return degrad_prime
def first_PK_stress_minus(F, psi_minus): F = fe.variable(F) energy_minus = psi_minus(F) P_minus = fe.diff(energy_minus, F) return P_minus
def const_eq(L_mod, p): return fe.diff(L_mod, p)
def stress(self, u): u_var = fe.variable(u) C = fe.variable(kin.right_cauchy_green(u_var)) return 2 * fe.diff(self.strain_energy(u_var), C)
def incompr_stress(g, F): return fe.diff(g, F)
def first_piola_stress(L, F): return -fe.diff(L, F)
def first_PK_stress(F, psi): F = fe.variable(F) energy = psi(F) P = fe.diff(energy, F) return P
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 cauchy_stress(epsilon, psi): epsilon = fe.variable(epsilon) energy = psi(epsilon) sigma = fe.diff(energy, epsilon) return sigma
# Not found rho = fe.Constant(1.1e3) c_10 = 6.352e-10 c_01 = 3.627 kappa = fe.Constant(0.01) # currently simplifications p_act = 0. lmb_f = 2. W_iso = c_10 * (I_1 - 3) + c_01 * (I_2 - 3) + 0.5 * kappa * (J - 1)**2 dx = fe.dx ds = fe.ds P = fe.diff(W_iso, F) S = 2 * fe.diff(W_iso, C) F_static = inner(S, DE(v)) * dx - rho * inner(b, v) * dx - inner( t_bar, v) * ds + (p / kappa + J - 1) * q * dx #F_static = inner(P, grad(v))*dx - rho*inner(b, v)*dx - inner(t_bar, v)*ds + (p/kappa + J - 1)*q*dx file_u = fe.File("../results/displacement.pvd") file_p = fe.File("../results/pressure.pvd") bcul = fe.DirichletBC(V.sub(0), u_left, left) bcur = fe.DirichletBC(V.sub(0), u_right, right) #bcpl = fe.DirichletBC(V.sub(1), p_left, left) bcs = [bcul, bcur] # bcpl] J_static = fe.derivative(F_static, w, dw)
def cauchy_stress_minus(epsilon, psi_minus): epsilon = fe.variable(epsilon) energy_minus = psi_minus(epsilon) sigma_minus = fe.diff(energy_minus, epsilon) return sigma_minus