navier-stokes_lshape.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """
  2. Solve the incompressible Navier-Stokes equations
  3. on an L-shaped domain using Chorin's splitting method.
  4. """
  5. from dolfin import *
  6. import numpy as np
  7. from vedo import download
  8. from vedo.dolfin import plot
  9. # Print log messages only from the root process in parallel
  10. parameters["std_out_all_processes"] = False
  11. # Load mesh from file
  12. fpath = download("https://vedo.embl.es/examples/data/lshape.xml.gz")
  13. mesh = Mesh(fpath)
  14. # Define function spaces (P2-P1)
  15. V = VectorFunctionSpace(mesh, "Lagrange", 2)
  16. Q = FunctionSpace(mesh, "Lagrange", 1)
  17. # Define trial and test functions
  18. u = TrialFunction(V)
  19. p = TrialFunction(Q)
  20. v = TestFunction(V)
  21. q = TestFunction(Q)
  22. # Set parameter values
  23. dt = 0.01
  24. T = 3
  25. nu = 0.01
  26. # Define time-dependent pressure boundary condition
  27. p_in = Expression("sin(3.0*t)", t=0.0, degree=2)
  28. # Define boundary conditions
  29. noslip = DirichletBC(
  30. V,
  31. (0, 0),
  32. (
  33. "on_boundary &&"
  34. "(x[0] < DOLFIN_EPS | x[1] < DOLFIN_EPS | "
  35. "(x[0] > 0.5 - DOLFIN_EPS && x[1] > 0.5 - DOLFIN_EPS))"
  36. ),
  37. )
  38. inflow = DirichletBC(Q, p_in, "x[1] > 1.0 - DOLFIN_EPS")
  39. outflow = DirichletBC(Q, 0, "x[0] > 1.0 - DOLFIN_EPS")
  40. bcu = [noslip]
  41. bcp = [inflow, outflow]
  42. # Create functions
  43. u0 = Function(V)
  44. u1 = Function(V)
  45. p1 = Function(Q)
  46. # Define coefficients
  47. k = Constant(dt)
  48. f = Constant((0, 0))
  49. # Tentative velocity step
  50. F1 = (
  51. (1 / k) * inner(u - u0, v) * dx
  52. + inner(grad(u0) * u0, v) * dx
  53. + nu * inner(grad(u), grad(v)) * dx
  54. - inner(f, v) * dx
  55. )
  56. a1 = lhs(F1)
  57. L1 = rhs(F1)
  58. # Pressure update
  59. a2 = inner(grad(p), grad(q)) * dx
  60. L2 = -(1 / k) * div(u1) * q * dx
  61. # Velocity update
  62. a3 = inner(u, v) * dx
  63. L3 = inner(u1, v) * dx - k * inner(grad(p1), v) * dx
  64. # Assemble matrices
  65. A1 = assemble(a1)
  66. A2 = assemble(a2)
  67. A3 = assemble(a3)
  68. # Use amg preconditioner if available
  69. prec = "amg" if has_krylov_solver_preconditioner("amg") else "default"
  70. # Use nonzero guesses - essential for CG with non-symmetric BC
  71. parameters["krylov_solver"]["nonzero_initial_guess"] = True
  72. # Time-stepping
  73. for t in np.arange(0, T, dt):
  74. # Update pressure boundary condition
  75. p_in.t = t
  76. # Compute tentative velocity step
  77. b1 = assemble(L1)
  78. [bc.apply(A1, b1) for bc in bcu]
  79. solve(A1, u1.vector(), b1, "bicgstab", "default")
  80. # Pressure correction
  81. b2 = assemble(L2)
  82. [bc.apply(A2, b2) for bc in bcp]
  83. [bc.apply(p1.vector()) for bc in bcp]
  84. solve(A2, p1.vector(), b2, "bicgstab", prec)
  85. # Velocity correction
  86. b3 = assemble(L3)
  87. [bc.apply(A3, b3) for bc in bcu]
  88. solve(A3, u1.vector(), b3, "bicgstab", "default")
  89. # Move to next time step
  90. u0.assign(u1)
  91. t += dt
  92. # Plot solution
  93. plotter = plot(
  94. u1,
  95. mode="mesh and arrows",
  96. text="Velocity of fluid",
  97. cmap="jet",
  98. scale=0.3, # unit conversion factor
  99. scalarbar=False,
  100. interactive=False,
  101. )
  102. plotter.remove("Arrows")