Example #1
0
    def setup(self):
        # Set up nonlinear CH solver based on provided linear solver
        factory = PETScFactory.instance()
        solver_ch = NewtonSolver(self.comm(), self.data["solver"]["CH"]["lin"],
                                 factory)
        solver_ch.parameters['absolute_tolerance'] = 1E-8
        solver_ch.parameters['relative_tolerance'] = 1E-16
        solver_ch.parameters['maximum_iterations'] = 25
        #solver_ch.parameters['relaxation_parameter'] = 1.0
        #solver_ch.parameters['error_on_nonconvergence'] = False
        #solver_ch.parameters['convergence_criterion'] = 'incremental'
        assert solver_ch.parameters["linear_solver"] == "user_defined"
        assert solver_ch.parameters["preconditioner"] == "user_defined"
        assert "nln" not in self.data["solver"]["CH"]
        self.data["solver"]["CH"]["nln"] = solver_ch

        # Set up NS solver
        if hasattr(self.data["solver"]["NS"], "ksp"):
            ksp = getattr(self.data["solver"]["NS"], "ksp")()
            if isinstance(ksp, PCDKSP):
                forms = self.data["forms"]
                bcs_ns = self.data["bcs_ns"]
                bc_pcd = self.data["model"].bcs()["pcd"]
                self.data["pcd_assembler"] = PCDAssembler(
                    forms["lin"]["lhs"],  # A
                    forms["lin"]["rhs"],  # b
                    bcs_ns,  # bcs
                    forms["pcd"]["a_pc"],  # P
                    gp=forms["pcd"]["gp"],  # B^T
                    ap=forms["pcd"]["ap"],
                    kp=forms["pcd"]["kp"],
                    mp=forms["pcd"]["mp"],
                    mu=forms["pcd"]["mu"],
                    bcs_pcd=bc_pcd)
                self.data["pcd_assembler"].get_pcd_form("mp").constant = False
                self.data["pcd_assembler"].get_pcd_form("mu").constant = False
                #self.data["pcd_assembler"].get_pcd_form("gp").phantom = False
                self._flags["init_pcd_called"] = False
        else:
            info("")
            info("'LUSolver' will be applied to the Navier-Stokes subproblem.")
            info("")
Example #2
0
    def __init__(self, *args, **kwargs):
        """
        Create nonlinear solver for
        :py:class:`Monolithic <muflon.functions.discretization.Monolithic>`
        discretization scheme.

        See :py:class:`Solver <muflon.solving.solvers.Solver>` for the list of
        valid initialization arguments.
        """
        super(Monolithic, self).__init__(*args, **kwargs)

        # Adjust forms
        DS = self.data["model"].discretization_scheme()
        w = DS.solution_ctl()[0]
        F = self.data["forms"]["nln"]
        J = derivative(F, w)

        # Adjust bcs
        bcs = []
        _bcs = self.data["model"].bcs()
        for bc_v in _bcs.get("v", []):
            assert isinstance(bc_v, tuple)
            assert len(bc_v) == len(w.sub(1))
            bcs += [bc for bc in bc_v if bc is not None]
        bcs += [bc for bc in _bcs.get("p", [])]
        # FIXME: Deal with possible bcs for ``phi`` and ``th``

        # Adjust solver
        solver = NewtonSolver()
        solver.parameters['absolute_tolerance'] = 1E-8
        solver.parameters['relative_tolerance'] = 1E-16
        solver.parameters['maximum_iterations'] = 10
        solver.parameters['linear_solver'] = "mumps"
        #solver.parameters['relaxation_parameter'] = 1.0

        # Preparation for tackling singular systems
        null_space = None
        if self._flags["fix_p"]:
            null_space, null_fcn = DS.build_pressure_null_space()
            self.data["null_fcn"] = null_fcn

        # Store solvers and collect other data
        self.data["solver"] = solver
        self.data["problem"] = Monolithic.Problem(F, bcs, J, null_space)
        self.data["sol_fcn"] = w
Example #3
0
# .. index::
#    single: Newton solver; (in Cahn-Hilliard demo)
#
# The DOLFIN Newton solver requires a
# :py:class:`NonlinearProblem<dolfin.cpp.NonlinearProblem>` object to
# solve a system of nonlinear equations. Here, we are using the class
# ``CahnHilliardEquation``, which was declared at the beginning of the
# file, and which is a sub-class of
# :py:class:`NonlinearProblem<dolfin.cpp.NonlinearProblem>`. We need to
# instantiate objects of both ``CahnHilliardEquation`` and
# :py:class:`NewtonSolver <dolfin.cpp.NewtonSolver>`::

# Create nonlinear problem and Newton solver
problem = CahnHilliardEquation(a, L)
solver = NewtonSolver(MPI.comm_world)
solver.convergence_criterion = "incremental"
solver.rtol = 1e-6

# The setting of ``convergence_criterion`` to ``"incremental"`` specifies
# that the Newton solver should compute a norm of the solution increment
# to check for convergence (the other possibility is to use
# ``"residual"``, or to provide a user-defined check). The tolerance for
# convergence is specified by ``rtol``.
#
# To run the solver and save the output to a VTK file for later
# visualization, the solver is advanced in time from :math:`t_{n}` to
# :math:`t_{n+1}` until a terminal time :math:`T` is reached::

# Output file
file = XDMFFile(MPI.comm_world, "output.xdmf")
Example #4
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
    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