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)
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
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)
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)