def initialize_data(self): """ Extract required objects for defining error control forms. This will be stored and reused. """ # Developer's note: The UFL-FFC-DOLFIN--PyDOLFIN toolchain for # error control is quite fine-tuned. In particular, the order # of coefficients in forms is (and almost must be) used for # their assignment. This means that the order in which these # coefficients are defined matters and should be considered # fixed. # Primal trial element space self._V = self.u.function_space() # Primal test space == Dual trial space Vhat = self.weak_residual.arguments()[0].function_space() # Discontinuous version of primal trial element space self._dV = tear(self._V) # Extract cell and geometric dimension mesh = self._V.mesh() dim = mesh.topology().dim() # Function representing improved dual E = increase_order(Vhat) self._Ez_h = Function(E) # Function representing cell bubble function B = FunctionSpace(mesh, "B", dim + 1) self._b_T = Function(B) self._b_T.vector()[:] = 1.0 # Function representing strong cell residual self._R_T = Function(self._dV) # Function representing cell cone function C = FunctionSpace(mesh, "DG", dim) self._b_e = Function(C) # Function representing strong facet residual self._R_dT = Function(self._dV) # Function for discrete dual on primal test space self._z_h = Function(Vhat) # Piecewise constants for assembling indicators self._DG0 = FunctionSpace(mesh, "DG", 0)
def _init_convenience(self, multimesh, family, degree): # Check arguments self.info = [family, degree] if not isinstance(family, string_types): cpp.dolfin_error( "multimeshfunctionspace.py", "create function space", "Illegal argument for finite element family, not a string: " + str(family)) if not isinstance(degree, int): cpp.dolfin_error( "multimeshfunctionspace.py", "create function space", "Illegal argument for degree, not an integer: " + str(degree)) if not isinstance(multimesh, cpp.mesh.MultiMesh): cpp.dolfin_error( "functionspace.py", "create multimesh function space", "Illegal argument, not a multimesh: " + str(multimesh)) # Create UFL element mesh = multimesh.part(0) element = ufl.FiniteElement(family, mesh.ufl_cell(), degree) # Create and add individual function spaces V = cpp.function.MultiMeshFunctionSpace(multimesh) V_parts = [] for part in range(multimesh.num_parts()): V_part = FunctionSpace(multimesh.part(part), element) V_parts.append(V_part) V.add(V_part) # Build multimesh function space V.build() # Store full function spaces self._parts = V_parts self._cpp_object = V
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one # with the next argument number form_arguments = form.arguments() number = max([-1] + [arg.number() for arg in form_arguments]) + 1 # NOTE : Mixed-domains problems need to have arg.part() != None # if any(arg.part() is not None for arg in form_arguments): # raise RuntimeError("Compute derivative of form, cannot automatically create new Argument using parts, please supply one") part = None if isinstance(u, Function): # u.part() is None except with mixed-domains part = u.part() V = u.function_space() du = Argument(V, number, part) elif isinstance(u, SpatialCoordinate): mesh = u.ufl_domain().ufl_cargo() element = u.ufl_domain().ufl_coordinate_element() V = FunctionSpace(mesh, element) du = Argument(V, number, part) elif isinstance(u, (list, tuple)) and all( isinstance(w, Function) for w in u): raise RuntimeError( "Taking derivative of form w.r.t. a tuple of Coefficients. Take derivative w.r.t. a single Coefficient on a mixed space instead." ) else: raise RuntimeError( "Computing derivative of form w.r.t. '{}'. Supply Function as a Coefficient" .format(u)) return ufl.derivative(form, u, du, coefficient_derivatives)
def set_poisson_eq_2d(n=8): ''' Basic example where only the mesh refinement is controlled. ''' # mesh mesh = UnitSquareMesh(n, n) # function space V = FunctionSpace(mesh, 'P', 1) # bcs u_D = Expression('1 + x[0]*x[0] + 2*x[1]*x[1]', degree=2) def boundary(x, on_boundary): return on_boundary bc = DirichletBC(V, u_D, boundary) # variational problem u = TrialFunction(V) v = TestFunction(V) f = Constant(-6.0) a = dot(grad(u), grad(v)) * dx L = f * v * dx # define unknown u = Function(V) return a, L, u, bc
def increase_order(V): """For a given function space, return the same space, but with a higher polynomial degree """ mesh = V.mesh() element = ufl.algorithms.elementtransformations.increase_order(V.ufl_element()) constrained_domain = V.dofmap().constrained_domain return FunctionSpace(mesh, element, constrained_domain=constrained_domain)
def set_linear_dirichlet_constant(mesh, dt, var_name='Temperature', bc_temperature=1300., temperature=300., source_value=0., axis=1, right=True, tol=1e-4, conductivity=1., density=1., specific_heat=1.): ''' Heat equation problem where an edge/surface is set at a given temperature and everything else at another. Parameters ---------- axis : int Controls boundary to which Dirichlet bc is applied. right : bool Dirichlet bc is applied to the planar and perpendicular face found in the max (True) or min (False) coordinate. ''' # function space V = FunctionSpace(mesh, 'Lagrange', 1) # boundary conditions bcs = [ set_dirichlet_bc_lim(V, value=bc_temperature, axis=axis, right=right, tol=tol) ] # initial condition u_initial = Constant(temperature) u_n = Function(V, name=var_name) u_n.interpolate(u_initial) # problem formulation f = Constant(source_value) u_n, a, L = get_formulation_constant_props(V, u_initial, f, dt, conductivity, density, specific_heat, var_name=var_name) u = Function(V, name=var_name) return u_n, a, L, u, bcs
def change_regularity(V, family): """For a given function space, return the corresponding space with the finite elements specified by 'family'. Possible families are the families supported by the form compiler """ mesh = V.mesh() element = ufl.algorithms.elementtransformations.change_regularity(V.ufl_element(), family) constrained_domain = V.dofmap().constrained_domain return FunctionSpace(mesh, element, constrained_domain=constrained_domain)
def set_linear_equal_opposite(mesh, dt, var_name='Temperature', axis=0, bc_temperature=300., source_value=0., conductivity=1., density=1., specific_heat=1., V=None): ''' Heat equation problem where two opposite edges/faces are set at the same temperature and the points in between follow a quadratic variation. Notes ----- * assumes opposite faces of interest are planar and perpendicular to the axis. ''' # function space if V is None: V = FunctionSpace(mesh, 'Lagrange', 1) # boundary conditions x_min, x_max = get_mesh_axis_lims(mesh, axis) bcs = [ set_dirichlet_bc(V, x, bc_temperature, axis) for x in [x_min, x_max] ] # initial condition c = (x_max - x_min) / 2 param = bc_temperature / (c**2) u_initial = Expression('param * (x[axis] - c) * (x[axis] - c)', degree=2, param=param, c=c, name='T', axis=axis) # problem formulation f = Constant(source_value) u_n, a, L = get_formulation_constant_props(V, u_initial, f, dt, conductivity, density, specific_heat, var_name=var_name) u = Function(V, name=var_name) return u_n, a, L, u, bcs
def formulate_laplacian(mesh, V=None): if V is None: V = FunctionSpace(mesh, 'CG', 1) u_h = TrialFunction(V) v = TestFunction(V) a = inner(grad(u_h), grad(v)) * dx b = inner(u_h, v) * dx dummy = inner(1., v) * dx return V, a, b, dummy
def _extract_function_space(expression, mesh): """Try to extract a suitable function space for projection of given expression. """ # Get mesh from expression if mesh is None: domain = expression.ufl_domain() if domain is not None: mesh = domain.ufl_cargo() # Extract mesh from functions if mesh is None: # (Not sure if this code is relevant anymore, the above code # should cover this) # Extract functions functions = ufl.algorithms.extract_coefficients(expression) for f in functions: if isinstance(f, Function): mesh = f.function_space().mesh() if mesh is not None: break if mesh is None: cpp.dolfin_error( "projection.py", "extract function space", "Unable to project expression, can't find " "a suitable mesh.") # Create function space shape = expression.ufl_shape if shape == (): V = FunctionSpace(mesh, "Lagrange", 1) elif len(shape) == 1: V = VectorFunctionSpace(mesh, "Lagrange", 1, dim=shape[0]) elif len(shape) == 2: V = TensorFunctionSpace(mesh, "Lagrange", 1, shape=shape) else: cpp.dolfin_error("projection.py", "extract function space", "Unhandled rank, shape is %s." % (shape, )) return V
def __init_from_ufl(self, multimesh, element): self.info = [element] if not isinstance(element, ufl.FiniteElementBase): cpp.dolfin_error( "multimeshfunctionspace.py", "create function space", "Illegal argument, not a finite element: " + str(element)) # Create and add individual function spaces V = cpp.function.MultiMeshFunctionSpace(multimesh) V_parts = [] for part in range(multimesh.num_parts()): V_part = FunctionSpace(multimesh.part(part), element) V_parts.append(V_part) V.add(V_part) # Build multimesh function space V.build() # Store full function spaces self._parts = V_parts self._cpp_object = V
def __init__(self, V: functionspace.FunctionSpace, x: typing.Optional[PETSc.Vec] = None, name: typing.Optional[str] = None): """Initialize finite element Function.""" # Create cpp Function if x is not None: self._cpp_object = cpp.function.Function(V._cpp_object, x) else: self._cpp_object = cpp.function.Function(V._cpp_object) # Initialize the ufl.FunctionSpace super().__init__(V.ufl_function_space(), count=self._cpp_object.id) # Set name if name is None: self.rename("f_{}".format(self.count())) else: self.rename(name) # Store DOLFIN FunctionSpace object self._V = V
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.function.GenericFunction): # cpp.dolfin_error("norms.py", # "compute error norm", # "Expecting a Function or Expression for u") # if not isinstance(uh, cpp.function.Function): # cpp.dolfin_error("norms.py", # "compute error norm", # "Expecting a Function for uh") # Get mesh if isinstance(u, cpp.function.Function) and mesh is None: mesh = u.function_space().mesh() if isinstance(uh, cpp.function.Function) and mesh is None: mesh = uh.function_space().mesh() if hasattr(uh, "_cpp_object") and mesh is None: mesh = uh._cpp_object.function_space().mesh() if hasattr(u, "_cpp_object") and mesh is None: mesh = u._cpp_object.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 norm(e, norm_type=norm_type, mesh=mesh)
def project(v, V=None, bcs=None, mesh=None, function=None, solver_type="lu", preconditioner_type="default", form_compiler_parameters=None): """Return projection of given expression *v* onto the finite element space *V*. *Arguments* v a :py:class:`Function <dolfin.functions.function.Function>` or an :py:class:`Expression <dolfin.functions.expression.Expression>` bcs Optional argument :py:class:`list of DirichletBC <dolfin.fem.bcs.DirichletBC>` V Optional argument :py:class:`FunctionSpace <dolfin.functions.functionspace.FunctionSpace>` mesh Optional argument :py:class:`mesh <dolfin.cpp.Mesh>`. solver_type see :py:func:`solve <dolfin.fem.solving.solve>` for options. preconditioner_type see :py:func:`solve <dolfin.fem.solving.solve>` for options. form_compiler_parameters see :py:class:`Parameters <dolfin.cpp.Parameters>` for more information. *Example of usage* .. code-block:: python v = Expression("sin(pi*x[0])") V = FunctionSpace(mesh, "Lagrange", 1) Pv = project(v, V) This is useful for post-processing functions or expressions which are not readily handled by visualization tools (such as for example discontinuous functions). """ # 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, dolfin.function.expression.Expression): if mesh is not None and isinstance(mesh, cpp.mesh.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) # Projection into a MultiMeshFunctionSpace if isinstance(V, MultiMeshFunctionSpace): # Create the measuresum of uncut and cut-cells dX = ufl.dx() + ufl.dC() # Define variational problem for projection w = TestFunction(V) Pv = TrialFunction(V) a = ufl.inner(w, Pv) * dX L = ufl.inner(w, v) * dX # Assemble linear system A = assemble_multimesh(a, form_compiler_parameters=form_compiler_parameters) b = assemble_multimesh(L, form_compiler_parameters=form_compiler_parameters) # Solve linear system for projection if function is None: function = MultiMeshFunction(V) cpp.la.solve(A, function.vector(), b, solver_type, preconditioner_type) return function # 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 w = TestFunction(V) Pv = TrialFunction(V) a = ufl.inner(w, Pv) * dx L = ufl.inner(w, v) * 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 leaf_node(self): u = self._cpp_object.leaf_node() return Function(FunctionSpace(u.function_space()), u.vector())
def function_space(self): "Return the FunctionSpace" return FunctionSpace(self._cpp_object.function_space())
def project(v, V=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 that adopts the :math:`r^2dr` measure as opposed to the standard Cartesian :math:`dx` measure. For documentation and usage, see the `original module <https://bitbucket.org/fenics-project/dolfin/src/master/python/dolfin/fem/projection.py>`_. .. 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. """ # 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.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) # 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 r2 = Expression('pow(x[0],2)', degree=func_degree) w = TestFunction(V) Pv = TrialFunction(V) a = ufl.inner(w, Pv) * r2 * dx L = ufl.inner(w, v) * r2 * 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 r2_errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None ): """ This function is a modification of FEniCS's built-in errornorm function that adopts the :math:`r^2dr` measure as opposed to the standard Cartesian :math:`dx` measure. For documentation and usage, see the original module <https://bitbucket.org/fenics-project/dolfin/src/master/python/dolfin/fem/norms.py>_. """ # Get mesh if isinstance(u, cpp.function.Function) and mesh is None: mesh = u.function_space().mesh() if isinstance(uh, cpp.function.Function) and mesh is None: mesh = uh.function_space().mesh() # if isinstance(uh, MultiMeshFunction) and mesh is None: # mesh = uh.function_space().multimesh() if hasattr(uh, "_cpp_object") and mesh is None: mesh = uh._cpp_object.function_space().mesh() if hasattr(u, "_cpp_object") and mesh is None: mesh = u._cpp_object.function_space().mesh() if mesh is None: raise RuntimeError("Cannot compute error norm. Missing mesh.") # Get rank if not u.ufl_shape == uh.ufl_shape: raise RuntimeError("Cannot compute error norm. Value shapes do not match.") shape = u.ufl_shape rank = len(shape) # Check that uh is associated with a finite element if uh.ufl_element().degree() is None: raise RuntimeError("Cannot 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 r2_norm(e, func_degree=degree, norm_type=norm_type, mesh=mesh )
def __init__(self, V: FunctionSpace, number: int, part: int = None): """Create a UFL/DOLFIN Argument""" ufl.Argument.__init__(self, V.ufl_function_space(), number, part) self._V = V