Ejemplo n.º 1
0
def errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None):
    """
    Compute and return the error :math:`e = u - u_h` in the given norm.

    *Arguments*
        u, uh
            :py:class:`Functions <dolfin.functions.function.Function>`
        norm_type
            Type of norm. The :math:`L^2` -norm is default.
            For other norms, see :py:func:`norm <dolfin.fem.norms.norm>`.
        degree_rise
            The number of degrees above that of u_h used in the
            interpolation; i.e. the degree of piecewise polynomials used
            to approximate :math:`u` and :math:`u_h` will be the degree
            of :math:`u_h` + degree_raise.
        mesh
            Optional :py:class:`Mesh <dolfin.cpp.Mesh>` on
            which to compute the error norm.

    In simple cases, one may just define

    .. code-block:: python

        e = u - uh

    and evalute for example the square of the error in the :math:`L^2` -norm by

    .. code-block:: python

        assemble(e*e*dx(), mesh)

    However, this is not stable w.r.t. round-off errors considering that
    the form compiler may expand the expression above to::

        e*e*dx() = u*u*dx() - 2*u*uh*dx() + uh*uh*dx()

    and this might get further expanded into thousands of terms for
    higher order elements. Thus, the error will be evaluated by adding
    a large number of terms which should sum up to something close to
    zero (if the error is small).

    This module computes the error by first interpolating both
    :math:`u` and :math:`u_h` to a common space (of high accuracy),
    then subtracting the two fields (which is easy since they are
    expressed in the same basis) and then evaluating the integral.

    """

    # Check argument
    if not isinstance(u, cpp.GenericFunction):
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Expecting a Function or Expression for u")
    if not isinstance(uh, cpp.Function):
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Expecting a Function for uh")

    # Get mesh
    if isinstance(u, Function) and mesh is None:
        mesh = u.function_space().mesh()
    if isinstance(uh, Function) and mesh is None:
        mesh = uh.function_space().mesh()
    if mesh is None:
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Missing mesh")

    # Get rank
    if not u.value_rank() == uh.value_rank():
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Value ranks don't match")
    rank = u.value_rank()

    # Check that uh is associated with a finite element
    if uh.ufl_element().degree() is None:
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Function uh must have a finite element")

    # Degree for interpolation space. Raise degree with respect to uh.
    degree = uh.ufl_element().degree() + degree_rise

    # Check degree of 'exact' solution u
    degree_u = u.ufl_element().degree()
    if degree_u is not None and degree_u < degree:
        cpp.warning("Degree of exact solution may be inadequate for accurate result in errornorm.")

    # Create function space
    if rank == 0:
        V = FunctionSpace(mesh, "Discontinuous Lagrange", degree)
    elif rank == 1:
        V = VectorFunctionSpace(mesh, "Discontinuous Lagrange", degree)
    else:
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Can't handle elements of rank %d" % rank)

    # Interpolate functions into finite element space
    pi_u = interpolate(u, V)
    pi_uh = interpolate(uh, V)

    # Compute the difference
    e = Function(V)
    e.assign(pi_u)
    e.vector().axpy(-1.0, pi_uh.vector())

    # Compute norm
    return norm(e, norm_type=norm_type, mesh=mesh)
Ejemplo n.º 2
0
def project(v,
            V=None,
            D=None,
            func_degree=None,
            bcs=None,
            mesh=None,
            function=None,
            solver_type="lu",
            preconditioner_type="default",
            form_compiler_parameters=None):
    """
    This function is a modification of FEniCS's built-in project function to adopt the :math:`r^2dr`
    measure as opposed to the standard Cartesian :math:`dx` one when the :math:`L_2` projection is 
    computed. I have removed the case for a multimesh function space as we don't use it.

    .. note:: Note the extra argument func_degree: this is used to interpolate the :math:`r^2` 
    Expression to the same degree as used in the definition of the Trial and Test function spaces.

    .. note:: I am manually implementing this bug fix that is not yet in the current release: 
              <https://bitbucket.org/fenics-project/dolfin/commits/c438724fa5d7f19504d4e0c48695e17b774b2c2d>_

    """

    # Try figuring out a function space if not specified
    if V is None:
        # Create function space based on Expression element if trying
        # to project an Expression
        if isinstance(v, Expression):
            # FIXME: Add handling of cpp.MultiMesh
            if mesh is not None and isinstance(mesh, cpp.Mesh):
                V = FunctionSpace(mesh, v.ufl_element())
            else:
                cpp.dolfin_error(
                    "projection.py", "perform projection",
                    "Expected a mesh when projecting an Expression")
        else:
            # Otherwise try extracting function space from expression
            V = _extract_function_space(v, mesh)

    # Check arguments
    if not isinstance(V, (FunctionSpace)):
        cpp.dolfin_error(
            "projection.py", "compute projection",
            "Illegal function space for projection, not a FunctionSpace:  " +
            str(v))

    # Ensure we have a mesh and attach to measure
    if mesh is None:
        mesh = V.mesh()
    dx = ufl.dx(mesh)

    # Define variational problem for projection

    # DS: HERE IS WHERE I MODIFY
    rD = Expression('pow(x[0],D-1)', D=D, degree=func_degree)

    w = TestFunction(V)
    Pv = TrialFunction(V)
    a = ufl.inner(w, Pv) * rD * dx
    L = ufl.inner(w, v) * rD * dx

    # Assemble linear system
    A, b = assemble_system(a,
                           L,
                           bcs=bcs,
                           form_compiler_parameters=form_compiler_parameters)

    # Solve linear system for projection
    if function is None:
        function = Function(V)
    cpp.la_solve(A, function.vector(), b, solver_type, preconditioner_type)

    return function
Ejemplo n.º 3
0
def errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None):
    """
    Compute and return the error :math:`e = u - u_h` in the given norm.

    *Arguments*
        u, uh
            :py:class:`Functions <dolfin.functions.function.Function>`
        norm_type
            Type of norm. The :math:`L^2` -norm is default.
            For other norms, see :py:func:`norm <dolfin.fem.norms.norm>`.
        degree_rise
            The number of degrees above that of u_h used in the
            interpolation; i.e. the degree of piecewise polynomials used
            to approximate :math:`u` and :math:`u_h` will be the degree
            of :math:`u_h` + degree_raise.
        mesh
            Optional :py:class:`Mesh <dolfin.cpp.Mesh>` on
            which to compute the error norm.

    In simple cases, one may just define

    .. code-block:: python

        e = u - uh

    and evalute for example the square of the error in the :math:`L^2` -norm by

    .. code-block:: python

        assemble(e**2*dx(mesh))

    However, this is not stable w.r.t. round-off errors considering that
    the form compiler may expand(#) the expression above to::

        e**2*dx = u**2*dx - 2*u*uh*dx + uh**2*dx

    and this might get further expanded into thousands of terms for
    higher order elements. Thus, the error will be evaluated by adding
    a large number of terms which should sum up to something close to
    zero (if the error is small).

    This module computes the error by first interpolating both
    :math:`u` and :math:`u_h` to a common space (of high accuracy),
    then subtracting the two fields (which is easy since they are
    expressed in the same basis) and then evaluating the integral.

    (#) If using the tensor representation optimizations.
    The quadrature represenation does not suffer from this problem.
    """

    # Check argument
    if not isinstance(u, cpp.GenericFunction):
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Expecting a Function or Expression for u")
    if not isinstance(uh, cpp.Function):
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Expecting a Function for uh")

    # Get mesh
    if isinstance(u, Function) and mesh is None:
        mesh = u.function_space().mesh()
    if isinstance(uh, Function) and mesh is None:
        mesh = uh.function_space().mesh()
    if mesh is None:
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Missing mesh")

    # Get rank
    if not u.shape() == uh.shape():
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Value shapes don't match")
    shape = u.shape()
    rank = len(shape)

    # Check that uh is associated with a finite element
    if uh.ufl_element().degree() is None:
        cpp.dolfin_error("norms.py",
                         "compute error norm",
                         "Function uh must have a finite element")

    # Degree for interpolation space. Raise degree with respect to uh.
    degree = uh.ufl_element().degree() + degree_rise

    # Check degree of 'exact' solution u
    degree_u = u.ufl_element().degree()
    if degree_u is not None and degree_u < degree:
        cpp.warning("Degree of exact solution may be inadequate for accurate result in errornorm.")

    # Create function space
    if rank == 0:
        V = FunctionSpace(mesh, "Discontinuous Lagrange", degree)
    elif rank == 1:
        V = VectorFunctionSpace(mesh, "Discontinuous Lagrange", degree, dim=shape[0])
    elif rank > 1:
        V = TensorFunctionSpace(mesh, "Discontinuous Lagrange", degree, shape=shape)

    # Interpolate functions into finite element space
    pi_u = interpolate(u, V)
    pi_uh = interpolate(uh, V)

    # Compute the difference
    e = Function(V)
    e.assign(pi_u)
    e.vector().axpy(-1.0, pi_uh.vector())

    # Compute norm
    return norm(e, norm_type=norm_type, mesh=mesh)
Ejemplo n.º 4
0
def rD_errornorm(u,
                 uh,
                 D=None,
                 func_degree=None,
                 norm_type="l2",
                 degree_rise=3,
                 mesh=None):
    r"""
    This function is a modification of FEniCS's built-in errornorm function to adopt the :math:`r^2dr`
    measure as opposed to the standard Cartesian :math:`dx` one. For documentation and usage, see the 
    `standard module <https://github.com/FEniCS/dolfin/blob/master/site-packages/dolfin/fem/norms.py>`_.

    Also see :func:`rD_norm`.

    """

    # Check argument
    if not isinstance(u, cpp.GenericFunction):
        cpp.dolfin_error("norms.py", "compute error norm",
                         "Expecting a Function or Expression for u")
    if not isinstance(uh, cpp.Function):
        cpp.dolfin_error("norms.py", "compute error norm",
                         "Expecting a Function for uh")

    # Get mesh
    if isinstance(u, Function) and mesh is None:
        mesh = u.function_space().mesh()
    if isinstance(uh, Function) and mesh is None:
        mesh = uh.function_space().mesh()
    if mesh is None:
        cpp.dolfin_error("norms.py", "compute error norm", "Missing mesh")

    # Get rank
    if not u.ufl_shape == uh.ufl_shape:
        cpp.dolfin_error("norms.py", "compute error norm",
                         "Value shapes don't match")
    shape = u.ufl_shape
    rank = len(shape)

    # Check that uh is associated with a finite element
    if uh.ufl_element().degree() is None:
        cpp.dolfin_error("norms.py", "compute error norm",
                         "Function uh must have a finite element")

    # Degree for interpolation space. Raise degree with respect to uh.
    degree = uh.ufl_element().degree() + degree_rise

    # Check degree of 'exact' solution u
    degree_u = u.ufl_element().degree()
    if degree_u is not None and degree_u < degree:
        cpp.warning(
            "Degree of exact solution may be inadequate for accurate result in errornorm."
        )

    # Create function space
    if rank == 0:
        V = FunctionSpace(mesh, "Discontinuous Lagrange", degree)
    elif rank == 1:
        V = VectorFunctionSpace(mesh,
                                "Discontinuous Lagrange",
                                degree,
                                dim=shape[0])
    elif rank > 1:
        V = TensorFunctionSpace(mesh,
                                "Discontinuous Lagrange",
                                degree,
                                shape=shape)

    # Interpolate functions into finite element space
    pi_u = interpolate(u, V)
    pi_uh = interpolate(uh, V)

    # Compute the difference
    e = Function(V)
    e.assign(pi_u)
    e.vector().axpy(-1.0, pi_uh.vector())

    # Compute norm
    return rD_norm(e,
                   D,
                   func_degree=func_degree,
                   norm_type=norm_type,
                   mesh=mesh)