elastodynamics.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. '''Time-integration of the
  2. elastodynamics equation
  3. '''
  4. from dolfin import *
  5. import numpy as np
  6. # Form compiler options
  7. parameters["form_compiler"]["cpp_optimize"] = True
  8. parameters["form_compiler"]["optimize"] = True
  9. # Define mesh
  10. mesh = BoxMesh(Point(0., 0., 0.), Point(1., 0.1, 0.04), 20, 5, 4)
  11. # Sub domain for clamp at left end
  12. def left(x, on_boundary):
  13. return near(x[0], 0.) and on_boundary
  14. # Sub domain for rotation at right end
  15. def right(x, on_boundary):
  16. return near(x[0], 1.) and on_boundary
  17. # Elastic parameters
  18. E = 800.0
  19. nu = 0.3
  20. mu = Constant(E / (2.0*(1.0 + nu)))
  21. lmbda = Constant(E*nu / ((1.0 + nu)*(1.0 - 2.0*nu)))
  22. # Mass density
  23. rho = Constant(1.0)
  24. # Rayleigh damping coefficients
  25. eta_m = Constant(0.)
  26. eta_k = Constant(0.)
  27. # Generalized-alpha method parameters
  28. alpha_m = Constant(0.2)
  29. alpha_f = Constant(0.4)
  30. gamma = Constant(0.5+alpha_f-alpha_m)
  31. beta = Constant((gamma+0.5)**2/4.)
  32. # Time-stepping parameters
  33. T = 4.0
  34. Nsteps = 50
  35. dt = Constant(T/Nsteps)
  36. p0 = 1.
  37. cutoff_Tc = T/5
  38. # Define the loading as an expression depending on t
  39. p = Expression(("0", "t <= tc ? p0*t/tc : 0", "0"), t=0, tc=cutoff_Tc, p0=p0, degree=0)
  40. # Define function space for displacement, velocity and acceleration
  41. V = VectorFunctionSpace(mesh, "CG", 1)
  42. # Define function space for stresses
  43. Vsig = TensorFunctionSpace(mesh, "DG", 0)
  44. # Test and trial functions
  45. du = TrialFunction(V)
  46. u_ = TestFunction(V)
  47. # Current (unknown) displacement
  48. u = Function(V, name="Displacement")
  49. # Fields from previous time step (displacement, velocity, acceleration)
  50. u_old = Function(V)
  51. v_old = Function(V)
  52. a_old = Function(V)
  53. # Create mesh function over the cell facets
  54. boundary_subdomains = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
  55. boundary_subdomains.set_all(0)
  56. force_boundary = AutoSubDomain(right)
  57. force_boundary.mark(boundary_subdomains, 3)
  58. # Define measure for boundary condition integral
  59. dss = ds(subdomain_data=boundary_subdomains)
  60. # Set up boundary condition at left end
  61. zero = Constant((0.0, 0.0, 0.0))
  62. bc = DirichletBC(V, zero, left)
  63. # Stress tensor
  64. def sigma(r):
  65. return 2.0*mu*sym(grad(r)) + lmbda*tr(sym(grad(r)))*Identity(len(r))
  66. # Mass form
  67. def m(u, u_):
  68. return rho*inner(u, u_)*dx
  69. # Elastic stiffness form
  70. def k(u, u_):
  71. return inner(sigma(u), sym(grad(u_)))*dx
  72. # Rayleigh damping form
  73. def c(u, u_):
  74. return eta_m*m(u, u_) + eta_k*k(u, u_)
  75. # Work of external forces
  76. def Wext(u_):
  77. return dot(u_, p)*dss(3)
  78. # Update formula for acceleration
  79. # a = 1/(2*beta)*((u - u0 - v0*dt)/(0.5*dt*dt) - (1-2*beta)*a0)
  80. def update_a(u, u_old, v_old, a_old, ufl=True):
  81. if ufl:
  82. dt_ = dt
  83. beta_ = beta
  84. else:
  85. dt_ = float(dt)
  86. beta_ = float(beta)
  87. return (u-u_old-dt_*v_old)/beta_/dt_**2 - (1-2*beta_)/2/beta_*a_old
  88. # Update formula for velocity
  89. # v = dt * ((1-gamma)*a0 + gamma*a) + v0
  90. def update_v(a, u_old, v_old, a_old, ufl=True):
  91. if ufl:
  92. dt_ = dt
  93. gamma_ = gamma
  94. else:
  95. dt_ = float(dt)
  96. gamma_ = float(gamma)
  97. return v_old + dt_*((1-gamma_)*a_old + gamma_*a)
  98. def update_fields(u, u_old, v_old, a_old):
  99. """Update fields at the end of each time step."""
  100. # Get vectors (references)
  101. u_vec, u0_vec = u.vector(), u_old.vector()
  102. v0_vec, a0_vec = v_old.vector(), a_old.vector()
  103. # use update functions using vector arguments
  104. a_vec = update_a(u_vec, u0_vec, v0_vec, a0_vec, ufl=False)
  105. v_vec = update_v(a_vec, u0_vec, v0_vec, a0_vec, ufl=False)
  106. # Update (u_old <- u)
  107. v_old.vector()[:], a_old.vector()[:] = v_vec, a_vec
  108. u_old.vector()[:] = u.vector()
  109. def avg(x_old, x_new, alpha):
  110. return alpha*x_old + (1-alpha)*x_new
  111. # Residual
  112. a_new = update_a(du, u_old, v_old, a_old, ufl=True)
  113. v_new = update_v(a_new, u_old, v_old, a_old, ufl=True)
  114. res = m(avg(a_old, a_new, alpha_m), u_) + c(avg(v_old, v_new, alpha_f), u_) \
  115. + k(avg(u_old, du, alpha_f), u_) - Wext(u_)
  116. a_form = lhs(res)
  117. L_form = rhs(res)
  118. # Define solver for reusing factorization
  119. K, res = assemble_system(a_form, L_form, bc)
  120. solver = LUSolver(K, "mumps")
  121. solver.parameters["symmetric"] = True
  122. # Time-stepping
  123. time = np.linspace(0, T, Nsteps+1)
  124. u_tip = np.zeros((Nsteps+1,))
  125. energies = np.zeros((Nsteps+1, 4))
  126. E_damp = 0
  127. E_ext = 0
  128. sig = Function(Vsig, name="sigma")
  129. #xdmf_file = XDMFFile("elastodynamics-results.xdmf")
  130. #xdmf_file.parameters["flush_output"] = True
  131. #xdmf_file.parameters["functions_share_mesh"] = True
  132. #xdmf_file.parameters["rewrite_function_mesh"] = False
  133. def local_project(v, V, u=None):
  134. """Element-wise projection using LocalSolver"""
  135. dv = TrialFunction(V)
  136. v_ = TestFunction(V)
  137. a_proj = inner(dv, v_)*dx
  138. b_proj = inner(v, v_)*dx
  139. solver = LocalSolver(a_proj, b_proj)
  140. solver.factorize()
  141. if u is None:
  142. u = Function(V)
  143. solver.solve_local_rhs(u)
  144. return u
  145. else:
  146. solver.solve_local_rhs(u)
  147. return
  148. ################################################################### time loop
  149. from vedo import Box, ProgressBar
  150. from vedo.dolfin import plot
  151. # add a frame box
  152. box = Box(length=1, width=1, height=1).pos(0.5,0,0).wireframe()
  153. pb = ProgressBar(0, len(np.diff(time)), c='blue')
  154. for (i, dt) in enumerate(np.diff(time)):
  155. t = time[i+1]
  156. # Forces are evaluated at t_{n+1-alpha_f}=t_{n+1}-alpha_f*dt
  157. p.t = t-float(alpha_f*dt)
  158. # Solve for new displacement
  159. res = assemble(L_form)
  160. bc.apply(res)
  161. solver.solve(K, u.vector(), res)
  162. # Update old fields with new quantities
  163. update_fields(u, u_old, v_old, a_old)
  164. # Save solution to XDMF format
  165. #xdmf_file.write(u, t)
  166. # Compute stresses and save to file
  167. local_project(sigma(u), Vsig, sig)
  168. #xdmf_file.write(sig, t)
  169. p.t = t
  170. # Record tip displacement and compute energies
  171. if MPI.comm_world.size == 1:
  172. u_tip[i+1] = u(1., 0.05, 0.)[1]
  173. E_elas = assemble(0.5*k(u_old, u_old))
  174. E_kin = assemble(0.5*m(v_old, v_old))
  175. E_damp += dt*assemble(c(v_old, v_old))
  176. # E_ext += assemble(Wext(u-u_old))
  177. E_tot = E_elas+E_kin+E_damp #-E_ext
  178. energies[i+1, :] = np.array([E_elas, E_kin, E_damp, E_tot])
  179. plot(u, box,
  180. mode='displace',
  181. style='matplotlib',
  182. axes=0, # no axes
  183. scalarbar=False,
  184. azimuth=1, # at each iteration add an angle to rotate scene
  185. text=__doc__, # add this file header
  186. interactive=False).clear()
  187. #screenshot('bar'+str(i)+'.png') # uncomment to save screenshots
  188. pb.print("Time: "+str(t)+" seconds")