Exemple #1
0
# 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.
Exemple #2
0
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
Exemple #3
0
# 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