Пример #1
0
    def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None):
        """
        Create linear variational problem a(u, v) = L(v).

        An optional argument bcs may be passed to specify boundary
        conditions.

        Another optional argument form_compiler_parameters may be
        specified to pass parameters to the form compiler.
        """

        # Extract and check arguments
        u = _extract_u(u)
        bcs = _extract_bcs(bcs)

        # Store input UFL forms and solution Function
        self.a_ufl = a
        self.L_ufl = L
        self.u_ufl = u

        # Store form compiler parameters
        form_compiler_parameters = form_compiler_parameters or {}
        self.form_compiler_parameters = form_compiler_parameters

        # Wrap forms (and check if linear form L is empty)
        if L.empty():
            L = cpp.Form(1, 0)
        else:
            L = Form(L, form_compiler_parameters=form_compiler_parameters)
        a = Form(a, form_compiler_parameters=form_compiler_parameters)

        # Initialize C++ base class
        cpp.LinearVariationalProblem.__init__(self, a, L, u, bcs)
Пример #2
0
def _solve_varproblem(*args, **kwargs):
    "Solve variational problem a == L or F == 0"

    # Extract arguments
    eq, u, bcs, J, tol, M, form_compiler_parameters, petsc_options \
        = _extract_args(*args, **kwargs)

    # Solve linear variational problem
    if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form):

        a = Form(eq.lhs, form_compiler_parameters=form_compiler_parameters)
        L = Form(eq.rhs, form_compiler_parameters=form_compiler_parameters)

        A = PETScMatrix()
        b = PETScVector()

        assembler = SystemAssembler(a, L, bcs)
        assembler.assemble(A, b)

        comm = L.mesh().mpi_comm()
        solver = PETScKrylovSolver(comm)

        solver.set_options_prefix("dolfin_solve_")
        for k, v in petsc_options.items():
            PETScOptions.set("dolfin_solve_" + k, v)
        solver.set_from_options()

        solver.set_operator(A)
        solver.solve(u.vector(), b)

    # Solve nonlinear variational problem
    else:

        raise RuntimeError("Not implemented")
Пример #3
0
    def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None):
        """Create linear variational problem a(u, v) = L(v).

        An optional argument bcs may be passed to specify boundary
        conditions.

        Another optional argument form_compiler_parameters may be
        specified to pass parameters to the form compiler.

        """

        if bcs is None:
            bcs = []
        elif not isinstance(bcs, (list, tuple)):
            bcs = [bcs]

        # Store input UFL forms and solution Function
        self.a_ufl = a
        self.L_ufl = L
        self.u_ufl = u

        # Store form compiler parameters
        form_compiler_parameters = form_compiler_parameters or {}
        self.form_compiler_parameters = form_compiler_parameters

        # Wrap forms (and check if linear form L is empty)
        if L.empty():
            L = cpp.fem.Form(1, 0)
        else:
            L = Form(L, form_compiler_parameters=form_compiler_parameters)
        a = Form(a, form_compiler_parameters=form_compiler_parameters)

        # Initialize C++ base class
        cpp.fem.LinearVariationalProblem.__init__(self, a, L, u._cpp_object, bcs)
Пример #4
0
    def __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None):
        """Create nonlinear variational problem F(u; v) = 0.

        Optional arguments bcs and J may be passed to specify boundary
        conditions and the Jacobian J = dF/du.

        Another optional argument form_compiler_parameters may be
        specified to pass parameters to the form compiler.

        """

        if bcs is None:
            bcs = []
        elif not isinstance(bcs, (list, tuple)):
            bcs = [bcs]

        # Store input UFL forms and solution Function
        self.F_ufl = F
        self.J_ufl = J
        self.u_ufl = u

        # Store form compiler parameters
        form_compiler_parameters = form_compiler_parameters or {}
        self.form_compiler_parameters = form_compiler_parameters

        # Wrap forms
        F = Form(F, form_compiler_parameters=form_compiler_parameters)
        if J is not None:
            J = Form(J, form_compiler_parameters=form_compiler_parameters)

        # Initialize C++ base class
        cpp.fem.NonlinearVariationalProblem.__init__(self, F, u._cpp_object, bcs, J)
Пример #5
0
    def __init__(self,
                 a,
                 L=None,
                 solver_type=cpp.fem.LocalSolver.SolverType.LU):
        """Create a local (cell-wise) solver for a linear variational problem
        a(u, v) = L(v).

        """

        # Store input UFL forms and solution Function
        self.a_ufl = a
        self.L_ufl = L

        # Wrap as DOLFIN forms
        a = Form(a)
        if L is None:
            # Initialize C++ base class
            cpp.fem.LocalSolver.__init__(self, a, solver_type)
        else:
            if L.empty():
                L = cpp.fem.Form(1, 0)
            else:
                L = Form(L)

            # Initialize C++ base class
            cpp.fem.LocalSolver.__init__(self, a, L, solver_type)
Пример #6
0
    def __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None):
        """
        Create nonlinear variational problem F(u; v) = 0.

        Optional arguments bcs and J may be passed to specify boundary
        conditions and the Jacobian J = dF/du.

        Another optional argument form_compiler_parameters may be
        specified to pass parameters to the form compiler.
        """

        # Extract and check arguments
        u = _extract_u(u)
        bcs = _extract_bcs(bcs)

        # Store input UFL forms and solution Function
        self.F_ufl = F
        self.J_ufl = J
        self.u_ufl = u

        # Store form compiler parameters
        form_compiler_parameters = form_compiler_parameters or {}
        self.form_compiler_parameters = form_compiler_parameters

        # Wrap forms
        F = Form(F, form_compiler_parameters=form_compiler_parameters)
        if J is not None:
            J = Form(J, form_compiler_parameters=form_compiler_parameters)

        # Initialize C++ base class
        cpp.NonlinearVariationalProblem.__init__(self, F, u, bcs, J)
Пример #7
0
    def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None):
        """Create mixed linear variational problem a(u, v) = L(v).

        An optional argument bcs may be passed to specify boundary
        conditions.

        Another optional argument form_compiler_parameters may be
        specified to pass parameters to the form compiler.

        """

        # Extract and check arguments (u is a list of Function)
        u_comps = [u[i]._cpp_object for i in range(len(u))]
        bcs = dolfin.fem.solving._extract_bcs(bcs)

        # Store form compiler parameters
        form_compiler_parameters = form_compiler_parameters or {}
        self.form_compiler_parameters = form_compiler_parameters

        # Update rhs if we don't have a consistent number of blocks
        if len(L) != len(u):
            L_tmp = [None for i in range(len(u))]
            for Li in L:
                L_tmp[Li.arguments()[0].part()] = Li
            L = L_tmp

        # Check number of blocks in lhs, rhs are consistent
        assert(len(a) == len(u) * len(u))
        assert(len(L) == len(u))

        # Create list of forms/blocks
        a_list = list()
        L_list = list()
        for Li in L:
            if Li is None:
                L_list.append([cpp.fem.Form(1, 0)])  # single-elt list
            elif Li.empty():
                L_list.append([cpp.fem.Form(1, 0)])  # single-elt list
            else:
                Ls = []  # List of Li subforms
                for Lsub in sub_forms_by_domain(Li):
                    if Lsub is None:
                        Ls.append(cpp.fem.Form(1, 0))
                    elif Lsub.empty():
                        Ls.append(cpp.fem.Form(1, 0))
                    else:
                        Ls.append(Form(Lsub, form_compiler_parameters=form_compiler_parameters))
                L_list.append(Ls)

        for ai in a:
            if ai is None:
                a_list.append([cpp.fem.Form(2, 0)])
            else:
                As = []
                for Asub in sub_forms_by_domain(ai):
                    As.append(Form(Asub, form_compiler_parameters=form_compiler_parameters))
                a_list.append(As)

        # Initialize C++ base class
        cpp.fem.MixedLinearVariationalProblem.__init__(self, a_list, L_list, u_comps, bcs)
Пример #8
0
def generate_error_control(problem, goal):
    """
    Create suitable ErrorControl object from problem and the goal

    *Arguments*

        problem (:py:class:`LinearVariationalProblem <dolfin.fem.solving.LinearVariationalProblem>`
        or :py:class:`NonlinearVariationalProblem <dolfin.fem.solving.NonlinearVariationalProblem>`)

            The (primal) problem

        goal (:py:class:`Form <dolfin.fem.form.Form>`)

            The goal functional

    *Returns*

        :py:class:`ErrorControl <dolfin.cpp.ErrorControl>`

    """
    # Generate UFL forms to be used for error control
    (ufl_forms, is_linear) = generate_error_control_forms(problem, goal)

    # Compile generated forms
    p = problem.form_compiler_parameters
    forms = [Form(form, form_compiler_parameters=p) for form in ufl_forms]

    # Create cpp.ErrorControl object
    forms += [is_linear]  # NOTE: Lingering design inconsistency.
    ec = cpp.adaptivity.ErrorControl(*forms)

    # Return generated ErrorControl
    return ec
Пример #9
0
def _create_cpp_form(form: typing.Union[Form, cpp.fem.Form]) -> cpp.fem.Form:
    """Create a compiled Form from a UFL form"""
    if form is None:
        return None
    elif isinstance(form, cpp.fem.Form):
        return form
    else:
        # FIXME: Attach cpp Form to UFL Form to avoid re-processing
        form = Form(form)
        return form._cpp_object
Пример #10
0
def _create_cpp_form(form, form_compiler_parameters=None):
    """Create a C++ Form from a UFL form"""
    if isinstance(form, cpp.fem.Form):
        if form_compiler_parameters is not None:
            cpp.warning(
                "Ignoring form_compiler_parameters when passed a dolfin Form!")
        return form
    elif isinstance(form, ufl.Form):
        form = Form(form, form_compiler_parameters=form_compiler_parameters)
        return form._cpp_object
    elif form is None:
        return None
    else:
        raise TypeError("Invalid form type: {}".format(type(form)))
Пример #11
0
def _create_cpp_form(form, form_compiler_parameters=None):
    # First check if we got a cpp.Form
    if isinstance(form, cpp.fem.Form):

        # Check that jit compilation has already happened
        if not hasattr(form, "_compiled_form"):
            raise TypeError(
                "Expected a dolfin form to have a _compiled_form attribute.")

        # Warn that we don't use the parameters if we get any
        if form_compiler_parameters is not None:
            cpp.warning(
                "Ignoring form_compiler_parameters when passed a dolfin Form!")
        return form
    elif isinstance(form, ufl.Form):
        form = Form(form, form_compiler_parameters=form_compiler_parameters)
        return form._cpp_object
    else:
        raise TypeError("Invalid form type %s" % (type(form), ))
Пример #12
0
    def __init__(self, problem, goal):
        """
        Create AdaptiveNonlinearVariationalSolver

        *Arguments*

            problem (:py:class:`NonlinearVariationalProblem <dolfin.fem.solving.NonlinearVariationalProblem>`)

        """

        # Store problem
        self.problem = problem

        # Generate error control object
        ec = generate_error_control(self.problem, goal)

        # Compile goal functional separately
        p = self.problem.form_compiler_parameters
        M = Form(goal, form_compiler_parameters=p)

        # Initialize C++ base class
        cpp.AdaptiveNonlinearVariationalSolver.__init__(self, problem, M, ec)
Пример #13
0
def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
    """
    Generates a list of forms and solutions for a given Butcher tableau 

    *Arguments*
        a (2 dimensional numpy array)
            The a matrix of the Butcher tableau.
        b (1-2 dimensional numpy array)
            The b vector of the Butcher tableau. If b is 2 dimensional the
            scheme includes an error estimator and can be used in adaptive
            solvers.
        c (1 dimensional numpy array)
            The c vector the Butcher tableau.
        time (_Constant_)
            A Constant holding the time at the start of the time step
        solution (_Function_)
            The prognostic variable
        rhs_form (ufl.Form)
            A UFL form representing the rhs for a time differentiated equation 
        adj (_Function_)
            The derivative of the functional with respect to y_n+1
    """

    a = _check_abc(a, b, c)
    size = a.shape[0]

    DX = _check_form(rhs_form)

    # Get test function
    arguments, coefficients = ufl.algorithms.\
                              extract_arguments_and_coefficients(rhs_form)
    v = arguments[0]

    # Create time step
    dt = Constant(0.1)

    # rhs forms
    dolfin_stage_forms = []
    ufl_stage_forms = []

    # Stage solutions
    k = [
        Function(solution.function_space(), name="k_%d" % i)
        for i in range(size)
    ]
    kbar = [Function(solution.function_space(), name="kbar_%d"%i) \
            for i in range(size)]

    # Create the stage forms
    y_ = solution
    ydot = solution.copy()
    time_ = time
    time_dep_expressions = _time_dependent_expressions(rhs_form, time)
    zero_ = ufl.zero(*y_.shape())
    forward_forms = []
    stage_solutions = []
    jacobian_indices = []

    # The recomputation of the forward run:
    for i, ki in enumerate(k):

        # Check whether the stage is explicit
        explicit = a[i, i] == 0

        # Evaluation arguments for the ith stage
        evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
                                  for j in range(i+1)], zero_)
        time = time_ + dt * c[i]

        replace_dict = _replace_dict_time_dependent_expression(\
            time_dep_expressions, time_, dt, c[i])

        replace_dict[y_] = evalargs
        replace_dict[time_] = time
        stage_form = ufl.replace(rhs_form, replace_dict)

        forward_forms.append(stage_form)

        if explicit:
            stage_forms = [stage_form]
            jacobian_indices.append(-1)
        else:
            # Create a F=0 form and differentiate it
            stage_form_implicit = stage_form - ufl.inner(ki, v) * DX
            stage_forms = [stage_form_implicit, derivative(\
                stage_form_implicit, ki)]
            jacobian_indices.append(0)

        ufl_stage_forms.append(stage_forms)
        dolfin_stage_forms.append([Form(form) for form in stage_forms])
        stage_solutions.append(ki)

    for i, kbari in reversed(list(enumerate(kbar))):

        # Check whether the stage is explicit
        explicit = a[i, i] == 0

        # And now the adjoint linearisation:
        stage_form_adm = ufl.inner(dt * b[i] * adj, v)*DX  + sum(\
            [dt * float(a[j,i]) * safe_action(safe_adjoint(derivative(\
                forward_forms[j], y_)), kbar[j]) for j in range(i, size)])
        if explicit:
            stage_forms_adm = [stage_form_adm]
            jacobian_indices.append(-1)
        else:
            # Create a F=0 form and differentiate it
            stage_form_adm -= ufl.inner(kbar[i], v) * DX
            stage_forms_adm = [
                stage_form_adm,
                derivative(stage_form_adm, kbari)
            ]
            jacobian_indices.append(1)

        ufl_stage_forms.append(stage_forms_adm)
        dolfin_stage_forms.append([Form(form) for form in stage_forms_adm])
        stage_solutions.append(kbari)

    # Only one last stage
    if len(b.shape) == 1:
        last_stage = Form(ufl.inner(adj, v)*DX + sum(\
            [safe_action(safe_adjoint(derivative(forward_forms[i], y_)), kbar[i]) \
             for i in range(size)]))
    else:
        raise Exception("Not sure what to do here")

    human_form = "unimplemented"

    return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage,\
           stage_solutions, dt, human_form, adj
Пример #14
0
def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
                                  perturbation):
    """
    Generates a list of forms and solutions for a given Butcher tableau 

    *Arguments*
        a (2 dimensional numpy array)
            The a matrix of the Butcher tableau.
        b (1-2 dimensional numpy array)
            The b vector of the Butcher tableau. If b is 2 dimensional the
            scheme includes an error estimator and can be used in adaptive
            solvers.
        c (1 dimensional numpy array)
            The c vector the Butcher tableau.
        time (_Constant_)
            A Constant holding the time at the start of the time step
        solution (_Function_)
            The prognostic variable
        rhs_form (ufl.Form)
            A UFL form representing the rhs for a time differentiated equation 
        perturbation (_Function_)
            The perturbation in the initial condition of the solution
    """

    a = _check_abc(a, b, c)
    size = a.shape[0]

    DX = _check_form(rhs_form)

    # Get test function
    arguments, coefficients = ufl.algorithms.\
                              extract_arguments_and_coefficients(rhs_form)
    v = arguments[0]

    # Create time step
    dt = Constant(0.1)

    # rhs forms
    dolfin_stage_forms = []
    ufl_stage_forms = []

    # Stage solutions
    k = [
        Function(solution.function_space(), name="k_%d" % i)
        for i in range(size)
    ]
    kdot = [Function(solution.function_space(), name="kdot_%d"%i) \
            for i in range(size)]

    # Create the stage forms
    y_ = solution
    ydot = solution.copy()
    time_ = time
    time_dep_expressions = _time_dependent_expressions(rhs_form, time)
    zero_ = ufl.zero(*y_.shape())
    forward_forms = []
    stage_solutions = []
    jacobian_indices = []

    for i, ki in enumerate(k):

        # Check whether the stage is explicit
        explicit = a[i, i] == 0

        # Evaluation arguments for the ith stage
        evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
                                  for j in range(i+1)], zero_)
        time = time_ + dt * c[i]

        replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions, \
                                                               time_, dt, c[i])

        replace_dict[y_] = evalargs
        replace_dict[time_] = time
        stage_form = ufl.replace(rhs_form, replace_dict)

        forward_forms.append(stage_form)

        # The recomputation of the forward run:

        if explicit:
            stage_forms = [stage_form]
            jacobian_indices.append(-1)
        else:
            # Create a F=0 form and differentiate it
            stage_form_implicit = stage_form - ufl.inner(ki, v) * DX
            stage_forms = [
                stage_form_implicit,
                derivative(stage_form_implicit, ki)
            ]
            jacobian_indices.append(0)

        ufl_stage_forms.append(stage_forms)
        dolfin_stage_forms.append([Form(form) for form in stage_forms])
        stage_solutions.append(ki)

        # And now the tangent linearisation:
        stage_form_tlm = safe_action(derivative(stage_form, y_), perturbation) + \
                         sum([dt*float(a[i,j]) * safe_action(derivative(\
            forward_forms[j], y_), kdot[j]) for j in range(i+1)])
        if explicit:
            stage_forms_tlm = [stage_form_tlm]
            jacobian_indices.append(-1)
        else:
            # Create a F=0 form and differentiate it
            stage_form_tlm -= ufl.inner(kdot[i], v) * DX
            stage_forms_tlm = [
                stage_form_tlm,
                derivative(stage_form_tlm, kdot[i])
            ]
            jacobian_indices.append(1)

        ufl_stage_forms.append(stage_forms_tlm)
        dolfin_stage_forms.append([Form(form) for form in stage_forms_tlm])
        stage_solutions.append(kdot[i])

    # Only one last stage
    if len(b.shape) == 1:
        last_stage = Form(ufl.inner(perturbation + sum(\
            [dt*float(bi)*kdoti for bi, kdoti in zip(b, kdot)], zero_), v)*DX)
    else:
        raise Exception("Not sure what to do here")

    human_form = []
    for i in range(size):
        kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \
                                         "%s*"% a[i,j], j) \
                           for j in range(size) if a[i,j] != 0)
        if c[i] in [0.0, 1.0]:
            cih = " + h" if c[i] == 1.0 else ""
        else:
            cih = " + %s*h" % c[i]

        kdotterm = " + ".join("%(a)sh*action(derivative(f(t_n%(cih)s, y_n + "\
                              "%(kterm)s), kdot_%(i)s" % \
                              {"a": ("" if a[i,j] == 1.0 else "%s*"% a[i,j], j),
                               "i": i,
                               "cih": cih,
                               "kterm": kterm} \
                              for j in range(size) if a[i,j] != 0)

        if len(kterm) == 0:
            human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {
                "i": i,
                "cih": cih
            })
            human_form.append("kdot_%(i)s = action(derivative("\
                              "f(t_n%(cih)s, y_n), y_n), ydot_n)" % \
                              {"i": i, "cih": cih})
        else:
            human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \
                          {"i": i, "cih": cih, "kterm": kterm})
            human_form.append("kdot_%(i)s = action(derivative(f(t_n%(cih)s, "\
                              "y_n + %(kterm)s), y_n) + %(kdotterm)s" % \
                          {"i": i, "cih": cih, "kterm": kterm, "kdotterm": kdotterm})

    parentheses = "(%s)" if np.sum(b > 0) > 1 else "%s"
    human_form.append("ydot_{n+1} = ydot_n + h*" + parentheses % (" + ".join(\
        "%skdot_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \
        for i in range(size) if b[i] > 0)))

    human_form = "\n".join(human_form)

    return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
           stage_solutions, dt, human_form, perturbation
Пример #15
0
def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
    """
    Generates a list of forms and solutions for a given Butcher tableau 

    *Arguments*
        a (2 dimensional numpy array)
            The a matrix of the Butcher tableau.
        b (1-2 dimensional numpy array)
            The b vector of the Butcher tableau. If b is 2 dimensional the
            scheme includes an error estimator and can be used in adaptive
            solvers.
        c (1 dimensional numpy array)
            The c vector the Butcher tableau.
        time (_Constant_)
            A Constant holding the time at the start of the time step
        solution (_Function_)
            The prognostic variable
        rhs_form (ufl.Form)
            A UFL form representing the rhs for a time differentiated equation 
    """

    a = _check_abc(a, b, c)
    size = a.shape[0]

    DX = _check_form(rhs_form)

    # Get test function
    arguments, coefficients = ufl.algorithms.\
                              extract_arguments_and_coefficients(rhs_form)
    v = arguments[0]

    # Create time step
    dt = Constant(0.1)

    # rhs forms
    dolfin_stage_forms = []
    ufl_stage_forms = []

    # Stage solutions
    k = [
        Function(solution.function_space(), name="k_%d" % i)
        for i in range(size)
    ]

    jacobian_indices = []

    # Create the stage forms
    y_ = solution
    time_ = time
    time_dep_expressions = _time_dependent_expressions(rhs_form, time)
    zero_ = ufl.zero(*y_.shape())
    for i, ki in enumerate(k):

        # Check whether the stage is explicit
        explicit = a[i, i] == 0

        # Evaluation arguments for the ith stage
        evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
                                  for j in range(i+1)], zero_)
        time = time_ + dt * c[i]

        replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions, \
                                                               time_, dt, c[i])

        replace_dict[y_] = evalargs
        replace_dict[time_] = time
        stage_form = ufl.replace(rhs_form, replace_dict)

        if explicit:
            stage_forms = [stage_form]
            jacobian_indices.append(-1)
        else:
            # Create a F=0 form and differentiate it
            stage_form -= ufl.inner(ki, v) * DX
            stage_forms = [stage_form, derivative(stage_form, ki)]
            jacobian_indices.append(0)
        ufl_stage_forms.append(stage_forms)

        dolfin_stage_forms.append([Form(form) for form in stage_forms])

    # Only one last stage
    if len(b.shape) == 1:
        last_stage = Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \
                                            zip(b, k)], zero_), v)*DX)
    else:
        # FIXME: Add support for addaptivity in RKSolver and MultiStageScheme

        last_stage = [Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \
                                             zip(b[0,:], k)], zero_), v)*DX),
                      Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \
                                             zip(b[1,:], k)], zero_), v)*DX)]

    # Create the Function holding the solution at end of time step
    #k.append(solution.copy())

    # Generate human form of MultiStageScheme
    human_form = []
    for i in range(size):
        kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \
                                         "%s*"% a[i,j], j) \
                           for j in range(size) if a[i,j] != 0)
        if c[i] in [0.0, 1.0]:
            cih = " + h" if c[i] == 1.0 else ""
        else:
            cih = " + %s*h" % c[i]

        if len(kterm) == 0:
            human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {
                "i": i,
                "cih": cih
            })
        else:
            human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \
                          {"i": i, "cih": cih, "kterm": kterm})

    parentheses = "(%s)" if np.sum(b > 0) > 1 else "%s"
    human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\
        "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \
        for i in range(size) if b[i] > 0)))

    human_form = "\n".join(human_form)

    return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
           k, dt, human_form, None
def _rush_larsen_scheme_generator(rhs_form, solution, time, order, generalized):
    """Generates a list of forms and solutions for a given Butcher
    tableau

    *Arguments*
        rhs_form (ufl.Form)
            A UFL form representing the rhs for a time differentiated equation
        solution (_Function_)
            The prognostic variable
        time (_Constant_)
            A Constant holding the time at the start of the time step
        order (int)
            The order of the scheme
        generalized (bool)
            If True generate a generalized Rush Larsen scheme, linearizing all
            components.

    """

    DX = _check_form(rhs_form)

    if DX != ufl.dP:
        raise TypeError("Expected a form with a Pointintegral.")

    # Create time step
    dt = Constant(0.1)

    # Get test function
    #    arguments = rhs_form.arguments()
    #    coefficients = rhs_form.coefficients()

    # Get time dependent expressions
    time_dep_expressions = _time_dependent_expressions(rhs_form, time)

    # Extract rhs expressions from form
    rhs_integrand = rhs_form.integrals()[0].integrand()
    rhs_exprs, v = extract_tested_expressions(rhs_integrand)
    vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1

    system_size = v.ufl_shape[0] if vector_rhs else 1

    # Fix for indexing of v for scalar expressions
    v = v if vector_rhs else [v]

    # Extract linear terms if not using generalized Rush Larsen
    if not generalized:
        linear_terms = _find_linear_terms(rhs_exprs, solution)
    else:
        linear_terms = [True for _ in range(system_size)]

    # Wrap the rhs expressions into a ufl vector type
    rhs_exprs = ufl.as_vector([rhs_exprs[i] for i in range(system_size)])
    rhs_jac = ufl.diff(rhs_exprs, solution)

    # Takes time!
    if vector_rhs:
        diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))
                          for ind in range(system_size)]
    else:
        diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[0]))]
        solution = [solution]

    ufl_stage_forms = []
    dolfin_stage_forms = []
    dt_stage_offsets = []

    # Stage solutions (3 per order rhs, linearized, and final step)
    # If 2nd order the final step for 1 step is a stage
    if order == 1:
        stage_solutions = []
        rl_ufl_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms,
                                        system_size, solution, None, dt, time, 1.0,
                                        0.0, v, DX, time_dep_expressions)
    elif order == 2:

        # Stage solution for order 2
        if vector_rhs:
            stage_solutions = [Function(solution.function_space(), name="y_1/2")]
        else:
            stage_solutions = [Function(solution[0].function_space(), name="y_1/2")]

        stage_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs,
                                       linear_terms, system_size,
                                       solution, None, dt, time, 0.5,
                                       0.0, v, DX,
                                       time_dep_expressions)

        rl_ufl_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs,
                                        linear_terms, system_size,
                                        solution, stage_solutions[0],
                                        dt, time, 1.0, 0.5, v, DX,
                                        time_dep_expressions)

        ufl_stage_forms.append([stage_form])
        dolfin_stage_forms.append([Form(stage_form)])

    # Get last stage form
    last_stage = Form(rl_ufl_form)

    human_form = "%srush larsen %s" % ("generalized " if generalized else "",
                                       str(order))

    return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \
        stage_solutions, dt, dt_stage_offsets, human_form, None
def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
                                      generalized, perturbation):
    """Generates a list of forms and solutions for the adjoint
    linearisation of the Rush-Larsen scheme

    *Arguments*
        rhs_form (ufl.Form)
            A UFL form representing the rhs for a time differentiated equation
        solution (_Function_)
            The prognostic variable
        time (_Constant_)
            A Constant holding the time at the start of the time step
        order (int)
            The order of the scheme
        generalized (bool)
            If True generate a generalized Rush Larsen scheme, linearizing all
            components.
        perturbation (Function)
            The vector on which we compute the adjoint action.

    """

    DX = _check_form(rhs_form)

    if DX != ufl.dP:
        raise TypeError("Expected a form with a Pointintegral.")

    # Create time step
    dt = Constant(0.1)

    # Get test function
    #    arguments = rhs_form.arguments()
    #    coefficients = rhs_form.coefficients()

    # Get time dependent expressions
    time_dep_expressions = _time_dependent_expressions(rhs_form, time)

    # Extract rhs expressions from form
    rhs_integrand = rhs_form.integrals()[0].integrand()
    rhs_exprs, v = extract_tested_expressions(rhs_integrand)
    vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1

    system_size = v.ufl_shape[0] if vector_rhs else 1

    # Fix for indexing of v for scalar expressions
    v = v if vector_rhs else [v]

    # Extract linear terms if not using generalized Rush Larsen
    if not generalized:
        linear_terms = _find_linear_terms(rhs_exprs, solution)
    else:
        linear_terms = [True for _ in range(system_size)]

    # Wrap the rhs expressions into a ufl vector type
    rhs_exprs = ufl.as_vector([rhs_exprs[i] for i in range(system_size)])
    rhs_jac = ufl.diff(rhs_exprs, solution)

    # Takes time!
    if vector_rhs:
        diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))
                          for ind in range(system_size)]
        soln = solution
    else:
        diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[0]))]
        solution = [solution]
        soln = solution[0]

    ufl_stage_forms = []
    dolfin_stage_forms = []
    dt_stage_offsets = []
    trial = TrialFunction(soln.function_space())

    # Stage solutions (3 per order rhs, linearized, and final step)
    # If 2nd order the final step for 1 step is a stage
    if order == 1:
        stage_solutions = []

        # Fetch the original step
        rl_ufl_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs,
                                        linear_terms, system_size,
                                        solution, None, dt, time, 1.0,
                                        0.0, v, DX,
                                        time_dep_expressions)

        # If this is commented out, we don't get NaNs.  Yhy is
        # solution a list of length zero anyway?
        rl_ufl_form = safe_action(safe_adjoint(derivative(rl_ufl_form, soln, trial)),
                                  perturbation)

    elif order == 2:
        # Stage solution for order 2
        fn_space = soln.function_space()

        stage_solutions = [Function(fn_space, name="y_1/2"),
                           Function(fn_space, name="y_1"),
                           Function(fn_space, name="y_bar_1/2")]

        y_half_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs,
                                        linear_terms, system_size,
                                        solution, None, dt, time, 0.5,
                                        0.0, v, DX,
                                        time_dep_expressions)

        y_one_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs,
                                       linear_terms, system_size,
                                       solution, stage_solutions[0],
                                       dt, time, 1.0, 0.5, v, DX,
                                       time_dep_expressions)

        y_bar_half_form = safe_action(safe_adjoint(derivative(y_one_form,
                                                              stage_solutions[0], trial)), perturbation)

        rl_ufl_form = safe_action(safe_adjoint(derivative(y_one_form, soln, trial)), perturbation) + \
            safe_action(safe_adjoint(derivative(y_half_form, soln, trial)), stage_solutions[2])

        ufl_stage_forms.append([y_half_form])
        ufl_stage_forms.append([y_one_form])
        ufl_stage_forms.append([y_bar_half_form])
        dolfin_stage_forms.append([Form(y_half_form)])
        dolfin_stage_forms.append([Form(y_one_form)])
        dolfin_stage_forms.append([Form(y_bar_half_form)])

    # Get last stage form
    last_stage = Form(rl_ufl_form)

    human_form = "%srush larsen %s" % ("generalized " if generalized else "",
                                       str(order))

    return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \
        stage_solutions, dt, dt_stage_offsets, human_form, perturbation
Пример #18
0
    def __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None):
        """Create nonlinear variational problem F(u; v) = 0.

        An optional argument bcs may be passed to specify boundary
        conditions.

        Another optional argument form_compiler_parameters may be
        specified to pass parameters to the form compiler.

        """

        # Extract and check arguments (u is a list of Function)
        u_comps = [u[i]._cpp_object for i in range(len(u))]
        bcs = dolfin.fem.solving._extract_bcs(bcs)

        # Store form compiler parameters
        form_compiler_parameters = form_compiler_parameters or {}
        self.form_compiler_parameters = form_compiler_parameters

        # Store input UFL forms and solution Function
        self.F_ufl = F
        self.J_ufl = J
        self.u_ufl = u

        # Update rhs if we don't have a consistent number of blocks
        if len(F) != len(u):
            F_tmp = [None for i in range(len(u))]
            for Fi in F:
                F_tmp[Fi.arguments()[0].part()] = Fi
            F = F_tmp

        # Check number of blocks in the residual and solution are coherent
        assert(len(J) == len(u) * len(u))
        assert(len(F) == len(u))

        # Create list of forms/blocks
        F_list = list()
        for Fi in F:
            if Fi is None:
                F_list.append([cpp.fem.Form(1, 0)])
            elif Fi.empty():
                F_list.append([cpp.fem.Form(1, 0)])  # single-elt list
            else:
                Fs = []
                for Fsub in sub_forms_by_domain(Fi):
                    if Fsub is None:
                        Fs.append(cpp.fem.Form(1, 0))
                    elif Fsub.empty():
                        Fs.append(cpp.fem.Form(1, 0))
                    else:
                        Fs.append(Form(Fsub, form_compiler_parameters=form_compiler_parameters))
                F_list.append(Fs)
        print("[problem] create list of residual forms OK")

        J_list = None
        if J is not None:
            J_list = list()
            for Ji in J:
                if Ji is None:
                    J_list.append([cpp.fem.Form(2, 0)])
                elif Ji.empty():
                    J_list.append([cpp.fem.Form(2, 0)])
                else:
                    Js = []
                    for Jsub in sub_forms_by_domain(Ji):
                        Js.append(Form(Jsub, form_compiler_parameters=form_compiler_parameters))
                    J_list.append(Js)
        print("[problem] create list of jacobian forms OK, J_list size = ", len(J_list))

        # Initialize C++ base class
        cpp.fem.MixedNonlinearVariationalProblem.__init__(self, F_list, u_comps, bcs, J_list)
Пример #19
0
def _butcher_scheme_generator(a, b, c, solution, rhs_form):
    """
    Generates a list of forms and solutions for a given Butcher tableau 

    *Arguments*
        a (2 dimensional numpy array)
            The a matrix of the Butcher tableau.
        b (1-2 dimensional numpy array)
            The b vector of the Butcher tableau. If b is 2 dimensional the
            scheme includes an error estimator and can be used in adaptive
            solvers.
        c (1 dimensional numpy array)
            The c vector the Butcher tableau.
        solution (_Function_)
            The prognastic variable
        rhs_form (ufl.Form)
            A UFL form representing the rhs for a time differentiated equation 
    """
    if not (isinstance(a, np.ndarray) and (len(a) == 1 or \
            (len(a.shape)==2 and a.shape[0] == a.shape[1]))):
        raise TypeError("Expected an m x m numpy array as the first argument")
    if not (isinstance(b, np.ndarray) and len(b.shape) in [1,2]):
        raise TypeError("Expected a 1 or 2 dimensional numpy array as the second argument")
    if not (isinstance(c, np.ndarray) and len(c.shape) == 1):
        raise TypeError("Expected a 1 dimensional numpy array as the third argument")

    # Make sure a is a "matrix"
    if len(a) == 1:
        a.shape = (1, 1)

    # Get size of system
    size = a.shape[0]

    # If b is a matrix we expect it to have two rows 
    if len(b.shape) == 2:
        if not (b.shape[0] == 2 and b.shape[1] == size):
            raise ValueError("Expected a 2 row matrix with the same number "\
                             "of collumns as the first dimension of the a matrix.")
    elif len(b) != size:
        raise ValueError("Expected the length of the b vector to have the "\
                         "same size as the first dimension of the a matrix.")
        
    if len(c) != size:
        raise ValueError("Expected the length of the c vector to have the "\
                         "same size as the first dimension of the a matrix.")

    # Check if tableau is fully implicit
    for i in range(size):
        for j in range(i):
            if a[j, i] != 0:
                raise ValueError("Does not support fully implicit Butcher tableau.")

    if not isinstance(rhs_form, ufl.Form):
        raise TypeError("Expected a ufl.Form as the 5th argument.")
        
    # Check if form contains a cell or point integral
    if "cell" in rhs_form.integral_groups():
        DX = ufl.dx
    elif "point" in rhs_form.integral_groups():
        DX = ufl.dP
    else:
        raise ValueError("Expected either a cell or point integral in the form.")
    
    # Get test function
    arguments, coefficients = ufl.algorithms.extract_arguments_and_coefficients(rhs_form)
    if len(arguments) != 1:
        raise ValueError("Expected the form to have rank 1")
    v = arguments[0]

    # Create time step
    dt = Constant(0.1)

    # rhs forms
    dolfin_stage_forms = []
    ufl_stage_forms = []
    
    # Stage solutions
    k = [solution.copy(deepcopy=True) for i in range(size)]

    # Create the stage forms
    y_ = solution
    for i, ki in enumerate(k):

        # Check wether the stage is explicit
        explicit = a[i,i] == 0

        # Evaluation arguments for the ith stage
        evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
                                  for j in range(i+1)], ufl.zero(*y_.shape()))
        
        stage_form = ufl.replace(rhs_form, {y_:evalargs})

        if explicit:
            stage_forms = [stage_form]
        else:
            # Create a F=0 form and differentiate it
            stage_form -= ufl.inner(ki, v)*DX
            stage_forms = [stage_form, derivative(stage_form, ki)]
        ufl_stage_forms.append(stage_forms)

        dolfin_stage_forms.append([Form(form) for form in stage_forms])
        
    # Only one last stage
    if len(b.shape) == 1:
        last_stage = cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b, k)])
    else:
        # FIXME: Add support for addaptivity in RKSolver and MultiStageScheme
        last_stage = [cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b[0,:], k)]),
                      cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b[1,:], k)])]

    # Create the Function holding the solution at end of time step
    #k.append(solution.copy())

    # Generate human form of MultiStageScheme
    human_form = []
    for i in range(size):
        kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \
                                         "%s*"% a[i,j], j) \
                           for j in range(size) if a[i,j] != 0)
        if c[i] in [0.0, 1.0]:
            cih = " + h" if c[i] == 1.0 else ""
        else:
            cih = " + %s*h" % c[i]
            
        if len(kterm) == 0:
            human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih})
        else:
            human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \
                          {"i": i, "cih": cih, "kterm": kterm})

    parentheses = "(%s)" if np.sum(b>0) > 1 else "%s"
    human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\
        "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \
        for i in range(size) if b[i] > 0)))

    human_form = "\n".join(human_form)
    
    return ufl_stage_forms, dolfin_stage_forms, last_stage, k, dt, human_form