Beispiel #1
0
def homogenize(bc):
    """
    Return a homogeneous version of the given boundary condition.

    *Arguments*
        bc
            a :py:class:`DirichletBC <dolfin.fem.bcs.DirichletBC>` instance,
            or a list/tuple of
            :py:class:`DirichletBC <dolfin.fem.bcs.DirichletBC>` instances.

    Other types of boundary conditions are ignored.

    If the given boundary condition is a list of boundary conditions,
    then a list of homogeneous boundary conditions is returned.

    """

    # Handle case when boundary condition is a list
    if isinstance(bc, (list, tuple)):
        bcs = bc
        return [homogenize(bc) for bc in bcs]

    # Only consider Dirichlet boundary conditions
    if not isinstance(bc, cpp.DirichletBC):
        cpp.dolfin_error("bcs.py", "homogenize boundary condition",
                         "Can only homogenize DirichletBCs")

    # Create zero function
    V = bc.function_space()
    if V.element().value_rank() == 0:
        zero = Constant(0)
    elif V.element().value_rank() == 1:
        zero = Constant([0] * V.element().value_dimension(0))
    else:
        cpp.dolfin_error("bcs.py",
                         "homogenize boundary condition",
                         "Unhandled value rank %d for homogenization of boundary conditions" % \
                             V.element().value_rank())

    # Create homogeneous boundary condition
    if len(bc.domain_args) == 1:
        new_bc = cpp.DirichletBC(V, zero, bc.domain_args[0])
    elif len(bc.domain_args) == 2:
        new_bc = cpp.DirichletBC(V, zero, bc.domain_args[0], bc.domain_args[1])
    else:
        cpp.dolfin_error("bcs.py", "homogenize boundary condition",
                         "Unknown type of boundary specification")
    new_bc.domain_args = bc.domain_args

    return new_bc
Beispiel #2
0
    def __init__(self, rhs_form, solution, t, bcs, a, b, c, order, \
                 generator=_butcher_scheme_generator):
        bcs = bcs or []
        t = t or Constant(0.0)
        ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
                         k, dt, human_form, contraction = generator(\
            a, b, c, t, solution, rhs_form)

        # Store data
        self._rhs_form = rhs_form
        self._ufl_stage_forms = ufl_stage_forms
        self._dolfin_stage_forms = dolfin_stage_forms
        self._t = t
        self._bcs = bcs
        self._dt = dt
        self._last_stage = last_stage
        self._solution = solution
        self._k = k
        self.a = a
        self.b = b
        self.c = c
        self._order = order
        self.contraction = contraction
        self.jacobian_indices = jacobian_indices

        cpp.MultiStageScheme.__init__(self, dolfin_stage_forms, last_stage, k, \
                                      solution, t, dt, c, jacobian_indices, order,\
                                      self.__class__.__name__,
                                      human_form, bcs)
Beispiel #3
0
    def __init__(self, *args, **kwargs):
        "Create Dirichlet boundary condition"

        # Copy constructor
        if len(args) == 1:
            if not isinstance(args[0], cpp.DirichletBC):
                cpp.dolfin_error("bcs.py",
                                 "create DirichletBC",
                                 "Expecting a DirichleBC as only argument"\
                                 " for copy constructor")

            # Initialize base class
            cpp.DirichletBC.__init__(self, args[0])
            self.domain_args = args[0].domain_args
            return

        # Special case for value specified as float, tuple or similar
        if len(args) >= 2 and not isinstance(args[1], cpp.GenericFunction):
            if isinstance(args[1], ufl.classes.Expr):
                expr = project(args[1], args[0])
            else:
                expr = Constant(args[1])  # let Constant handle all problems
            args = args[:1] + (expr, ) + args[2:]

        # Special case for sub domain specified as a function
        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            sub_domain = AutoSubDomain(args[2])
            args = args[:2] + (sub_domain, ) + args[3:]

        # Special case for sub domain specified as a string
        if len(args) >= 3 and isinstance(args[2], str):
            sub_domain = CompiledSubDomain(args[2])
            args = args[:2] + (sub_domain, ) + args[3:]

        # Store Expression to avoid scoping issue with SWIG directors
        if isinstance(args[1], cpp.Expression):
            self.function_arg = args[1]

        # Store SubDomain to avoid scoping issue with SWIG directors
        self.domain_args = args[2:]

        # FIXME: Handling of multiple default arguments does not
        # really work between Python and C++ when C++ requires them to
        # be ordered and cannot accept the latter without the
        # former...

        # Add keyword arguments (in correct order...)
        allowed_kwargs = ["method", "check_midpoint"]
        for key in allowed_kwargs:
            if key in kwargs:
                args = tuple(list(args) + [kwargs[key]])

        # Check for other keyword arguments
        for key in kwargs:
            if not key in allowed_kwargs:
                cpp.dolfin_error("bcs.py", "create boundary condition",
                                 "Unknown keyword argument \"%s\"" % key)

        # Initialize base class
        cpp.DirichletBC.__init__(self, *args)
Beispiel #4
0
    def __init__(self,
                 rhs_form,
                 solution,
                 time,
                 bcs,
                 a,
                 b,
                 c,
                 order,
                 generator=_butcher_scheme_generator):
        bcs = bcs or []
        time = time or Constant(0.0)
        ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
                         stage_solutions, dt, human_form, contraction = \
                         generator(a, b, c, time, solution, rhs_form)

        # Store data
        self.a = a
        self.b = b
        self.c = c

        MultiStageScheme.__init__(self, rhs_form, ufl_stage_forms,
                                  dolfin_stage_forms, last_stage,
                                  stage_solutions, solution, time, dt,
                                  c, jacobian_indices, order,\
                                  self.__class__.__name__, human_form,
                                  bcs, contraction)
Beispiel #5
0
    def __init__(self,
                 rhs_form,
                 solution,
                 time,
                 order,
                 generalized,
                 generator=_rush_larsen_scheme_generator):

        # FIXME: What with bcs?
        bcs = []
        time = time or Constant(0.0)
        if order not in [1, 2]:
            raise ValueError("Expected order to be either 1 or 2")

        rhs_form, ufl_stage_forms, linear_terms, dofin_stage_forms, last_stage, \
                  stage_solutions, dt, dt_stage_offsets, human_form, contraction = \
                  generator(rhs_form, solution, time, order, generalized)

        self.linear_terms = linear_terms

        # All stages are explicit
        jacobian_indices = [-1 for _ in stage_solutions]

        # Highjack a and b to hold parameters for RushLarsen scheme generation
        MultiStageScheme.__init__(self, rhs_form, ufl_stage_forms,
                                  dofin_stage_forms, last_stage,
                                  stage_solutions, solution, time, dt,
                                  dt_stage_offsets, jacobian_indices, order,
                                  self.__class__.__name__, human_form, bcs,
                                  contraction)
Beispiel #6
0
def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, \
                      y0, stage_solution, dt, time, a, c, v, DX, time_dep_expressions):

    # If we need to replace the original solution with stage solution
    repl = None
    if stage_solution is not None:

        if system_size > 1:
            repl = {y0: stage_solution}
        else:
            repl = {y0[0]: stage_solution}

    # If we have time dependent expressions
    if time_dep_expressions and abs(float(c)) > DOLFIN_EPS:
        time_ = time
        time = time + dt * float(c)

        repl.update(_replace_dict_time_dependent_expression(time_dep_expressions, \
                                                            time_, dt, float(c)))
        repl[time_] = time

    # If all terms are linear (using generalized=True) we add a safe
    # guard to the linearized term. See below
    safe_guard = sum(linear_terms) == system_size

    # Add componentwise contribution to rl form
    rl_ufl_form = ufl.zero()
    num_rl_steps = 0
    for ind in range(system_size):

        # forward euler step
        fe_du_i = rhs_exprs[ind] * dt * float(a)

        # If exact integration
        if linear_terms[ind]:
            num_rl_steps += 1

            # Rush Larsen step
            # Safeguard the divisor: let's hope diff_rhs_exprs[ind] is never 1.0e-16!
            # Let's get rid of this when the conditional fixes land properly in UFL.
            eps = Constant(1.0e-16)
            rl_du_i = rhs_exprs[ind]/(diff_rhs_exprs[ind] + eps)*(\
                ufl.exp(diff_rhs_exprs[ind]*dt) - 1.0)

            # If safe guard
            if safe_guard:
                du_i = ufl.conditional(ufl.lt(abs(diff_rhs_exprs[ind]), 1e-8),
                                       fe_du_i, rl_du_i)
            else:
                du_i = rl_du_i
        else:
            du_i = fe_du_i

        # If we should replace solution in form with stage solution
        if repl:
            du_i = ufl.replace(du_i, repl)

        rl_ufl_form += (y0[ind] + du_i) * v[ind]

    return rl_ufl_form * DX
Beispiel #7
0
def _find_linear_terms(rhs_exprs, u):
    """
    Help function that takes a list of rhs expressions and return a
    list of bools determining what component, rhs_exprs[i], is linear
    wrt u[i].
    """

    uu = [Constant(1.0) for _ in rhs_exprs]
    if len(rhs_exprs) > 1:
        repl = {u: ufl.as_vector(uu)}
    else:
        repl = {u: uu[0]}

    linear_terms = []
    for i, ui in enumerate(uu):
        comp_i_s = expand_indices(ufl.replace(rhs_exprs[i], repl))
        linear_terms.append(ui in extract_coefficients(comp_i_s) and \
                            ui not in extract_coefficients(\
                                expand_derivatives(ufl.diff(comp_i_s, ui))))
    return linear_terms
    def __init__(self, rhs_form, solution, t, bcs, a, b, c, order):
        bcs = bcs or []
        t = t or Constant(0.0)
        ufl_stage_forms, dolfin_stage_forms, last_stage, k, dt, human_form = \
                         _butcher_scheme_generator(a, b, c, solution, rhs_form)

        # Store data
        self._rhs_form = rhs_form
        self._ufl_stage_forms = ufl_stage_forms
        self._dolfin_stage_forms = dolfin_stage_forms
        self._t = t
        self._dt = dt
        self._last_stage = last_stage
        self._solution = solution
        self._k = k
        self.a = a
        self.b = b
        self.c = c

        cpp.MultiStageScheme.__init__(self, dolfin_stage_forms, last_stage, k, \
                                      solution, t, dt, c, order,
                                      self.__class__.__name__,
                                      human_form, bcs)
Beispiel #9
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
Beispiel #10
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
Beispiel #11
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 _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
Beispiel #13
0
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