# Step in time t = 0.0 # Check if we are running on CI server and reduce run time if "CI" in os.environ.keys(): T = 3 * dt else: T = 50 * dt u.vector.copy(result=u0.vector) u0.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) while (t < T): t += dt r = solver.solve(problem, u.vector) print("Step, num iterations:", int(t / dt), r[0]) u.vector.copy(result=u0.vector) file.write(u.sub(0), t) # The string ``"compressed"`` indicates that the output data should be # compressed to reduce the file size. Within the time stepping loop, the # solution vector associated with ``u`` is copied to ``u0`` at the # beginning of each time step, and the nonlinear problem is solved by # calling # :py:func:`solver.solve(problem,u.vector)<dolfin.cpp.NewtonSolver.solve>`, # with the new solution vector returned in # :py:func:`u.vector<dolfin.cpp.Function.vector>`. The ``c`` component # of the solution (the first component of ``u``) is then written to file # at every time step.
def compute_tentative_velocity( time_step_method, rho, mu, u, p0, dt, u_bcs, f, W, my_dx, tol ): """Compute the tentative velocity via .. math:: \\rho (u_0 + (u\\cdot\\nabla)u) = \\mu \\frac{1}{r} \\div(r \\nabla u) + \\rho g. """ class TentativeVelocityProblem(NonlinearProblem): def __init__(self, ui, time_step_method, rho, mu, u, p0, dt, bcs, f, my_dx): super(TentativeVelocityProblem, self).__init__() W = ui.function_space() v = TestFunction(W) self.bcs = bcs r = SpatialCoordinate(ui.function_space().mesh())[0] def me(uu, ff): return _momentum_equation(uu, v, p0, ff, rho, mu, my_dx) self.F0 = rho * dot(ui - u[0], v) / dt * 2 * pi * r * my_dx if time_step_method == "forward euler": self.F0 += me(u[0], f[0]) elif time_step_method == "backward euler": self.F0 += me(ui, f[1]) else: assert ( time_step_method == "crank-nicolson" ), "Unknown time stepper '{}'".format( time_step_method ) self.F0 += 0.5 * (me(u[0], f[0]) + me(ui, f[1])) self.jacobian = derivative(self.F0, ui) self.reset_sparsity = True return # pylint: disable=unused-argument def F(self, b, x): # We need to evaluate F at x, so we have to make sure that self.F0 # is assembled for ui=x. We could use a self.ui and set # # self.ui.vector()[:] = x # # here. One way around this copy is to instantiate this class with # the same Function ui that is then used for the solver.solve(). assemble(self.F0, tensor=b, form_compiler_parameters={"optimize": True}) for bc in self.bcs: bc.apply(b, x) return def J(self, A, x): # We can ignore x; see comment at F(). assemble( self.jacobian, tensor=A, form_compiler_parameters={"optimize": True} ) for bc in self.bcs: bc.apply(A) self.reset_sparsity = False return solver = NewtonSolver() solver.parameters["maximum_iterations"] = 10 solver.parameters["absolute_tolerance"] = tol solver.parameters["relative_tolerance"] = 0.0 solver.parameters["report"] = True # While GMRES+ILU converges if the time step is small enough, increasing # the time step slows down convergence dramatically in some cases. This # makes the step fail, and the adaptive time stepper will decrease the step # size. This size can be _very_ small such that simulation take forever. # For now, just use a direct solver. Choose UMFPACK over SuperLU since the # docker image doesn't contain SuperLU yet, cf. # <https://bitbucket.org/fenics-project/docker/issues/64/add-superlu>. # TODO come up with an appropriate GMRES preconditioner here solver.parameters["linear_solver"] = "umfpack" ui = Function(W) step_problem = TentativeVelocityProblem( ui, time_step_method, rho, mu, u, p0, dt, u_bcs, f, my_dx ) # Take u[0] as initial guess. ui.assign(u[0]) solver.solve(step_problem, ui.vector()) # Make sure ui is from W. This should happen anyways, but somehow doesn't. # TODO find out why not ui = project(ui, W) # div_u = 1/r * div(r*ui) return ui
# Step in time t = 0.0 # Check if we are running on CI server and reduce run time if "CI" in os.environ.keys(): T = 3 * dt else: T = 50 * dt u.vector().copy(result=u0.vector()) u0.vector().ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) while (t < T): t += dt solver.solve(problem, u.vector()) u.vector().copy(result=u0.vector()) file.write(u.sub(0), t) # The string ``"compressed"`` indicates that the output data should be # compressed to reduce the file size. Within the time stepping loop, the # solution vector associated with ``u`` is copied to ``u0`` at the # beginning of each time step, and the nonlinear problem is solved by # calling # :py:func:`solver.solve(problem,u.vector())<dolfin.cpp.NewtonSolver.solve>`, # with the new solution vector returned in # :py:func:`u.vector()<dolfin.cpp.Function.vector>`. The ``c`` component # of the solution (the first component of ``u``) is then written to file # at every time step.
def step(self, dt, u1, p1, u, p0, u_bcs, p_bcs, f0=None, f1=None, verbose=True, tol=1.0e-10 ): '''General pressure projection scheme as described in section 3.4 of :cite:`GMS06`. ''' # Some initial sanity checkups. assert dt > 0.0 # Define coefficients k = Constant(dt) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Compute tentative velocity step. # rho (u[-1] + (u.\nabla)u) = mu 1/r \div(r \nabla u) + rho g. with Message('Computing tentative velocity'): solver = NewtonSolver() solver.parameters['maximum_iterations'] = 5 solver.parameters['absolute_tolerance'] = tol solver.parameters['relative_tolerance'] = 0.0 solver.parameters['report'] = True # The nonlinear term makes the problem generally nonsymmetric. solver.parameters['linear_solver'] = 'gmres' # If the nonsymmetry is too strong, e.g., if u[-1] is large, then # AMG preconditioning might not work very well. # Use HYPRE-Euclid instead of ILU for parallel computation. solver.parameters['preconditioner'] = 'hypre_euclid' solver.parameters['krylov_solver']['relative_tolerance'] = tol solver.parameters['krylov_solver']['absolute_tolerance'] = 0.0 solver.parameters['krylov_solver']['maximum_iterations'] = 1000 solver.parameters['krylov_solver']['monitor_convergence'] = verbose ui = Function(self.W) step_problem = \ TentativeVelocityProblem(ui, self.theta, self.rho, self.mu, u, p0, k, u_bcs, f0, f1, stabilization=self.stabilization, dx=self.dx ) # Take u[-1] as initial guess. ui.assign(u[-1]) solver.solve(step_problem, ui.vector()) #div_u = 1/r * div(r*ui) #plot(div_u) #interactive() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - with Message('Computing pressure correction'): # The pressure correction is based on the update formula # # ( dphi/dr ) # rho/dt (u_{n+1}-u*) + ( dphi/dz ) = 0 # (1/r dphi/dtheta) # # with # # phi = p_{n+1} - p* # # and # # 1/r d/dr (r ur_{n+1}) # + d/dz ( uz_{n+1}) # + 1/r d/dtheta( utheta_{n+1}) = 0 # # With the assumption that u does not change in the direction # theta, one derives # # - 1/r * div(r * \nabla phi) = 1/r * rho/dt div(r*(u_{n+1} - u*)) # - 1/r * n. (r * \nabla phi) = 1/r * rho/dt n.(r*(u_{n+1} - u*)) # # In its weak form, this is # # \int r * \grad(phi).\grad(q) *2*pi = # - rho/dt \int div(r*u*) q *2*p # - rho/dt \int_Gamma n.(r*(u_{n+1}-u*)) q *2*pi. # # (The terms 1/r cancel with the volume elements 2*pi*r.) # If Dirichlet boundary conditions are applied to both u* and u_n # (the latter in the final step), the boundary integral vanishes. # alpha = 1.0 #alpha = 3.0 / 2.0 rotational_form = False self._pressure_poisson(p1, p0, self.mu, ui, self.rho * alpha * ui / k, p_bcs=p_bcs, rotational_form=rotational_form, tol=tol, verbose=verbose ) #plot(ui, title='u intermediate') ##plot(f, title='f') ##plot(ui[1], title='u intermediate[1]') #plot(div(ui), title='div(u intermediate)') #plot(p1, title='p1') #interactive() # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Velocity correction. # U = u[-1] - dt/rho \nabla (p1-p0). with Message('Computing velocity correction'): u = TrialFunction(self.W) v = TestFunction(self.W) a3 = dot(u, v) * dx phi = Function(self.P) phi.assign(p1) if p0: phi -= p0 if rotational_form: r = Expression('x[0]', degree=1, domain=self.W.mesh()) div_ui = 1/r * (r * ui[0]).dx(0) + ui[1].dx(1) phi += self.mu * div_ui L3 = dot(ui, v) * dx \ - k / self.rho * (phi.dx(0) * v[0] + phi.dx(1) * v[1]) * dx # Jacobi preconditioning for mass matrix is order-optimal. solve( a3 == L3, u1, bcs=u_bcs, solver_parameters={ 'linear_solver': 'iterative', 'symmetric': True, 'preconditioner': 'jacobi', 'krylov_solver': {'relative_tolerance': tol, 'absolute_tolerance': 0.0, 'maximum_iterations': 100, 'monitor_convergence': verbose} } ) #u = project(ui - k/rho * grad(phi), V) # div_u = 1/r * div(r*u) r = Expression('x[0]', degree=1, domain=self.W.mesh()) div_u1 = 1.0 / r * (r * u1[0]).dx(0) + u1[1].dx(1) info('||u||_div = %e' % sqrt(assemble(div_u1 * div_u1 * dx))) #plot(div_u) #interactive() ## Ha! TODO remove #u1.vector()[:] = ui.vector() return