def jit(form, form_compiler_parameters=None, common_cell=None): """Just-in-time compile any provided form. It uses the jit function from the form compiler registered by parameters["form_compiler"]["name"]. """ # Check that form is not empty if isinstance(form, ufl.Form): if form.integrals() == (): raise RuntimeError, "Form is empty. Cannot pass to JIT compiler." # Import form compiler form_compiler_name = cpp.parameters["form_compiler"]["name"] try: form_compiler = __import__(form_compiler_name) except ImportError, message: print message warning("Could not import %s form compiler, falling back to FFC." % form_compiler_name) try: form_compiler = __import__("ffc") except: cpp.dolfin_error("jit.py", "perform just-in-time compilation of form", "Could not import FFC form compiler")
def _plot_cpp(object, mesh, kwargs): # Convert kwargs to cpp format p = cpp.Parameters() for key in kwargs: try: p.add(key, kwargs[key]) except TypeError: cpp.warning("Incompatible type for keyword argument \"%s\". Ignoring." % key) if isinstance(object, cpp.Expression): plot_object = cpp.plot(object, mesh, p) elif isinstance(object, cpp.MultiMesh): return cpp.plot_multimesh(object) else: plot_object = cpp.plot(object, p) # Compatibility with book plot_object.write_ps = _VTKPlotter_write_ps # Avoid premature deletion of plotted objects if they go out of scope # before the plot window is closed. The plotter itself is safe, since it's # created in the plot() C++ function, not directly from Python. But the # Python plotter proxy may disappear, so we can't store the references # there. global _objects_referenced_from_plot_windows _objects_referenced_from_plot_windows[plot_object.key()] = (object, mesh, p) return plot_object
def _plot_matplotlib(obj, mesh, kwargs): if isinstance(obj, (cpp.MultiMesh, cpp.MultiMeshFunction, cpp.MultiMeshDirichletBC)): cpp.warning("Don't know how to plot type %s" % type(obj)) return # Avoid importing pyplot until used try: import matplotlib.pyplot as plt except: print("*** Warning: matplotlib.pyplot not available, cannot plot") return gdim = mesh.geometry().dim() if gdim == 3 or kwargs.get("mode") in ("warp",): # Importing this toolkit has side effects enabling 3d support from mpl_toolkits.mplot3d import axes3d # Enabling the 3d toolbox requires some additional arguments ax = plt.gca(projection='3d') else: ax = plt.gca() ax.set_aspect('equal') title = kwargs.pop("title", None) if title is not None: ax.set_title(title) # Translate range_min/max kwargs supported by VTKPlotter vmin = kwargs.pop("range_min", None) vmax = kwargs.pop("range_max", None) if vmin and not "vmin" in kwargs: kwargs["vmin"] = vmin if vmax and not "vmax" in kwargs: kwargs["vmax"] = vmax # Let's stays consistent and use mode='color' instead of 'surface' if kwargs.get("mode") == "surface": cpp.deprecation("plot kwarg mode='surface'", "1.7.0", "1.7.0", "Use mode='color' instead.") kwargs["mode"] = "color" # Drop unsupported kwargs and inform user _unsupported_kwargs = ["interactive", "rescale", "wireframe"] for kw in _unsupported_kwargs: if kwargs.pop(kw, None): cpp.warning("Matplotlib backend does not support '%s' kwarg yet. " "Ignoring it..." % kw) if isinstance(obj, cpp.Function): return mplot_function(ax, obj, **kwargs) elif isinstance(obj, cpp.Expression): return mplot_expression(ax, obj, mesh, **kwargs) elif isinstance(obj, cpp.Mesh): return mplot_mesh(ax, obj, **kwargs) elif isinstance(obj, cpp.DirichletBC): return mplot_dirichletbc(ax, obj, **kwargs) elif isinstance(obj, _meshfunction_types): return mplot_meshfunction(ax, obj, **kwargs) else: raise AttributeError('Failed to plot %s' % type(obj))
def _plot_matplotlib(obj, mesh, kwargs): if not isinstance(obj, _matplotlib_plottable_types): print("Don't know how to plot type %s." % type(obj)) return # Plotting is not working with all ufl cells if mesh.ufl_cell().cellname() not in ['interval', 'triangle', 'tetrahedron']: raise AttributeError(("Matplotlib plotting backend doesn't handle %s mesh.\n" "Possible options are saving the output to XDMF file " "or using 'x3dom' backend.") % mesh.ufl_cell().cellname()) # Avoid importing pyplot until used try: import matplotlib.pyplot as plt except Exception: cpp.warning("matplotlib.pyplot not available, cannot plot.") return gdim = mesh.geometry().dim() if gdim == 3 or kwargs.get("mode") in ("warp",): # Importing this toolkit has side effects enabling 3d support from mpl_toolkits.mplot3d import axes3d # noqa # Enabling the 3d toolbox requires some additional arguments ax = plt.gca(projection='3d') else: ax = plt.gca() ax.set_aspect('equal') title = kwargs.pop("title", None) if title is not None: ax.set_title(title) # Translate range_min/max kwargs supported by VTKPlotter vmin = kwargs.pop("range_min", None) vmax = kwargs.pop("range_max", None) if vmin and "vmin" not in kwargs: kwargs["vmin"] = vmin if vmax and "vmax" not in kwargs: kwargs["vmax"] = vmax # Drop unsupported kwargs and inform user _unsupported_kwargs = ["rescale", "wireframe"] for kw in _unsupported_kwargs: if kwargs.pop(kw, None): cpp.warning("Matplotlib backend does not support '%s' kwarg yet. " "Ignoring it..." % kw) if isinstance(obj, cpp.function.Function): return mplot_function(ax, obj, **kwargs) elif isinstance(obj, cpp.function.Expression): return mplot_expression(ax, obj, mesh, **kwargs) elif isinstance(obj, cpp.mesh.Mesh): return mplot_mesh(ax, obj, **kwargs) elif isinstance(obj, cpp.fem.DirichletBC): return mplot_dirichletbc(ax, obj, **kwargs) elif isinstance(obj, _meshfunction_types): return mplot_meshfunction(ax, obj, **kwargs) else: raise AttributeError('Failed to plot %s' % type(obj))
def _plot_x3dom(obj, kwargs): if not isinstance(obj, _x3dom_plottable_types): cpp.warning("Don't know how to plot type %s." % type(obj)) return x3dom = dolfin.X3DOM() out = x3dom.html(obj) return out
def _plot_matplotlib(obj, mesh, kwargs): if isinstance( obj, (cpp.MultiMesh, cpp.MultiMeshFunction, cpp.MultiMeshDirichletBC)): cpp.warning("Don't know how to plot type %s" % type(obj)) return # Avoid importing pyplot until used try: import matplotlib.pyplot as plt except: print("*** Warning: matplotlib.pyplot not available, cannot plot") return gdim = mesh.geometry().dim() if gdim == 3 or kwargs.get("mode") in ("warp", ): # Importing this toolkit has side effects enabling 3d support from mpl_toolkits.mplot3d import axes3d # Enabling the 3d toolbox requires some additional arguments ax = plt.gca(projection='3d') else: ax = plt.gca() ax.set_aspect('equal') title = kwargs.pop("title", None) if title is not None: ax.set_title(title) # Translate range_min/max kwargs supported by VTKPlotter vmin = kwargs.pop("range_min", None) vmax = kwargs.pop("range_max", None) if vmin and not "vmin" in kwargs: kwargs["vmin"] = vmin if vmax and not "vmax" in kwargs: kwargs["vmax"] = vmax # Drop unsupported kwargs and inform user _unsupported_kwargs = ["interactive", "rescale", "wireframe"] for kw in _unsupported_kwargs: if kwargs.pop(kw, None): cpp.warning("Matplotlib backend does not support '%s' kwarg yet. " "Ignoring it..." % kw) if isinstance(obj, cpp.Function): return mplot_function(ax, obj, **kwargs) elif isinstance(obj, cpp.Expression): return mplot_expression(ax, obj, mesh, **kwargs) elif isinstance(obj, cpp.Mesh): return mplot_mesh(ax, obj, **kwargs) elif isinstance(obj, cpp.DirichletBC): return mplot_dirichletbc(ax, obj, **kwargs) elif isinstance(obj, _meshfunction_types): return mplot_meshfunction(ax, obj, **kwargs) else: raise AttributeError('Failed to plot %s' % type(obj))
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)))
def _create_dolfin_form(form, form_compiler_parameters=None): # First check if we got a cpp.Form if isinstance(form, cpp.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): return Form(form, form_compiler_parameters=form_compiler_parameters) else: raise TypeError("Invalid form type %s" % (type(form),))
def mplot_meshfunction(ax, obj, **kwargs): mesh = obj.mesh() tdim = mesh.topology().dim() d = obj.dim() if tdim == 2 and d == 2: C = obj.array() triang = mesh2triang(mesh) assert not kwargs.pop("facecolors", None), "Not expecting 'facecolors' in kwargs" return ax.tripcolor(triang, facecolors=C, **kwargs) else: # Return gracefully to make regression test pass without vtk cpp.warning('Matplotlib plotting backend does not support mesh ' 'function of dim %d. Continuing without plotting...' % d) return
def jit(form, form_compiler_parameters=None, mpi_comm=None): """Just-in-time compile any provided form. It uses the jit function from the form compiler registered by parameters["form_compiler"]["name"]. """ # Check that form is not empty if isinstance(form, ufl.Form): if form.empty(): cpp.dolfin_error("jit.py", "perform just-in-time compilation of form", "Form is empty. Cannot pass to JIT compiler") # Import form compiler form_compiler_name = cpp.parameters["form_compiler"]["name"] try: form_compiler = __import__(form_compiler_name) except ImportError as message: print(message) warning("Could not import %s form compiler, falling back to FFC." \ % form_compiler_name) try: form_compiler = __import__("ffc") except: cpp.dolfin_error("jit.py", "perform just-in-time compilation of form", "Could not import FFC form compiler") # Checks on form compiler interface if not (hasattr(form_compiler, 'default_parameters') and hasattr(form_compiler, 'jit')): cpp.dolfin_error("jit.py", "perform just-in-time compilation of form", "Form compiler must implement the default_parameters " "and jit functions") # Prepare form compiler parameters p = form_compiler.default_parameters() # Set parameters from global DOLFIN parameter set for key, value in six.iteritems(parameters["form_compiler"]): p[key] = value # Override with local parameters if any if form_compiler_parameters: p.update(form_compiler_parameters) # Execute! return form_compiler.jit(form, parameters=p)
def _create_dolfin_form(form, form_compiler_parameters=None): # First check if we got a cpp.Form if isinstance(form, cpp.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): return Form(form, form_compiler_parameters=form_compiler_parameters) else: raise TypeError("Invalid form type %s" % (type(form), ))
def jit(form, form_compiler_parameters=None, common_cell=None): """Just-in-time compile any provided form. It uses the jit function from the form compiler registered by parameters["form_compiler"]["name"]. """ # Check that form is not empty if isinstance(form, ufl.Form): if form.integrals() == (): raise RuntimeError, "Form is empty. Cannot pass to JIT compiler." global _swig_version_ok # Check and set swig binary if not _swig_version_ok and not check_and_set_swig_binary(\ parameters["swig_binary"], parameters["swig_path"]): raise OSError, "Could not find swig installation. Pass an existing "\ "swig binary or install SWIG version 2.0 or higher.\n" # Check that the form compiler will use the same swig version # that PyDOLFIN was compiled with _swig_version_ok = _swig_version_ok or \ check_swig_version(cpp.__swigversion__, same=True) if not _swig_version_ok: raise OSError, """\ PyDOLFIN was not compiled with the present version of swig. Install swig version %s or recompiled PyDOLFIN with present swig """%cpp.__swigversion__ # Cache swig version test _swig_version_ok = True # Import form compiler form_compiler_name = cpp.parameters["form_compiler"]["name"] try: form_compiler = __import__(form_compiler_name) except ImportError, message: print message warning("Could not import %s form compiler, falling back to FFC." % form_compiler_name) try: form_compiler = __import__("ffc") except: cpp.dolfin_error("jit.py", "perform just-in-time compilation of form", "Could not import FFC form compiler")
def _has_matplotlib(): try: import matplotlib except ImportError: return False # Switch to Agg backend if DISPLAY not set if not os.environ.get("DISPLAY"): cpp.log(cpp.PROGRESS, "Environment variable DISPLAY not set. Switching " "to 'Agg' matplotlib backend.") try: matplotlib.use("Agg") except ValueError as e: cpp.warning("Switching to 'Agg' backend failed with the message:") print('"%s"' % str(e)) cpp.warning("Trying to continue...") return True
def mplot_function(ax, f, **kwargs): mesh = f.function_space().mesh() gdim = mesh.geometry().dim() tdim = mesh.topology().dim() # Extract the function vector in a way that also works for # subfunctions try: fvec = f.vector() except RuntimeError: fspace = f.function_space() try: fspace = fspace.collapse() # Happens for part of MultiMeshFunction; no way detecting elsewhere except RuntimeError: cpp.warning("Probably trying to plot MultiMeshFunction " "part. Continuing without plotting...") return fvec = dolfin.interpolate(f, fspace).vector() if fvec.size() == mesh.num_cells(): # DG0 cellwise function C = fvec.array() # NB! Assuming here dof ordering matching cell numbering if gdim == 2 and tdim == 2: return ax.tripcolor(mesh2triang(mesh), C, **kwargs) elif gdim == 3 and tdim == 2: # surface in 3d # FIXME: Not tested, probably broken xy = mesh.coordinates() shade = kwargs.pop("shade", True) return ax.plot_trisurf(mesh2triang(mesh), xy[:,2], C, shade=shade, **kwargs) elif gdim == 1 and tdim == 1: x = mesh.coordinates()[:,0] nv = len(x) # Insert duplicate points to get piecewise constant plot xp = np.zeros(2*nv-2) xp[0] = x[0] xp[-1] = x[-1] xp[1:2*nv-3:2] = x[1:-1] xp[2:2*nv-2:2] = x[1:-1] Cp = np.zeros(len(xp)) Cp[0:len(Cp)-1:2] = C Cp[1:len(Cp):2] = C return ax.plot(xp, Cp, *kwargs) #elif tdim == 1: # FIXME: Plot embedded line else: raise AttributeError('Matplotlib plotting backend only supports 2D mesh for scalar functions.') elif f.value_rank() == 0: # Scalar function, interpolated to vertices # TODO: Handle DG1? C = f.compute_vertex_values(mesh) if gdim == 2 and tdim == 2: mode = kwargs.pop("mode", "color") if mode == "color": shading = kwargs.pop("shading", "flat") return ax.tripcolor(mesh2triang(mesh), C, shading=shading, **kwargs) elif mode == "warp": from matplotlib import cm cmap = kwargs.pop("cmap", cm.jet) linewidths = kwargs.pop("linewidths", 0) return ax.plot_trisurf(mesh2triang(mesh), C, cmap=cmap, linewidths=linewidths, **kwargs) elif mode == "wireframe": return ax.triplot(mesh2triang(mesh), **kwargs) elif mode == "contour": return ax.tricontour(mesh2triang(mesh), C, **kwargs) elif gdim == 3 and tdim == 2: # surface in 3d # FIXME: Not tested from matplotlib import cm cmap = kwargs.pop("cmap", cm.jet) return ax.plot_trisurf(mesh2triang(mesh), C, cmap=cmap, **kwargs) elif gdim == 3 and tdim == 3: # Volume # TODO: Isosurfaces? # Vertex point cloud X = [mesh.coordinates()[:, i] for i in range(gdim)] return ax.scatter(*X, c=C, **kwargs) elif gdim == 1 and tdim == 1: x = mesh.coordinates()[:,0] ax.set_aspect('auto') # Setting limits for Line2D objects vmin = kwargs.pop("vmin", None) vmax = kwargs.pop("vmax", None) ax.set_ylim([vmin, vmax]) return ax.plot(x, C, **kwargs) #elif tdim == 1: # FIXME: Plot embedded line else: raise AttributeError('Matplotlib plotting backend only supports 2D mesh for scalar functions.') elif f.value_rank() == 1: # Vector function, interpolated to vertices w0 = f.compute_vertex_values(mesh) nv = mesh.num_vertices() if len(w0) != gdim*nv: raise AttributeError('Vector length must match geometric dimension.') X = mesh.coordinates() X = [X[:, i] for i in range(gdim)] U = [w0[i*nv: (i+1)*nv] for i in range(gdim)] # Compute magnitude C = U[0]**2 for i in range(1,gdim): C += U[i]**2 C = np.sqrt(C) mode = kwargs.pop("mode", "glyphs") if mode == "glyphs": args = X + U + [C] if gdim == 3: # 3d quiver plot works only since matplotlib 1.4 import matplotlib if StrictVersion(matplotlib.__version__) < '1.4': cpp.warning('Matplotlib version %s does not support 3d ' 'quiver plot. Continuing without plotting...' % matplotlib.__version__) return length = kwargs.pop("length", 0.1) return ax.quiver(*args, length=length, **kwargs) else: return ax.quiver(*args, **kwargs) elif mode == "displacement": Xdef = [X[i] + U[i] for i in range(gdim)] import matplotlib.tri as tri if gdim == 2 and tdim == 2: # FIXME: Not tested triang = tri.Triangulation(Xdef[0], Xdef[1], mesh.cells()) shading = kwargs.pop("shading", "flat") return ax.tripcolor(triang, C, shading=shading, **kwargs) else: # Return gracefully to make regression test pass without vtk cpp.warning('Matplotlib plotting backend does not support ' 'displacement for %d in %d. Continuing without ' 'plotting...' % (tdim, gdim)) return
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, form, form_compiler_parameters=None, subdomains=None): "Create JIT-compiled form from any given form (compiled or not)." # Check form argument if isinstance(form, ufl.Form): # Cutoff for better error message than ffc would give if not form.empty() and not form.domains(): raise RuntimeError("Expecting a completed form with domains at this point.") # Extract subdomain data sd = form.subdomain_data() if len(sd) != 1: # Until the assembler and ufc has multimesh support, we impose this limitation raise RuntimeError("Expecting subdomain data for a single domain only.") if subdomains is None: self.subdomains, = list(sd.values()) # Assuming single domain domain, = list(sd.keys()) # Assuming single domain mesh = domain.data() # Jit the module, this will take some time... self._compiled_form, module, prefix \ = jit(form, form_compiler_parameters, mpi_comm=mesh.mpi_comm()) # Extract function spaces of form arguments self.function_spaces = [func.function_space() for func in form.arguments()] # Extract coefficients from form (pass only the ones that the compiled form actually uses to the assembler) original_coefficients = form.coefficients() self.coefficients = [original_coefficients[self._compiled_form.original_coefficient_position(i)] for i in range(self._compiled_form.num_coefficients())] elif isinstance(form, cpp.Form): cpp.deprecation("Passing a cpp.Form to dolfin.Form constructor", "1.3.0", "1.4.0", "Passing a cpp.Form to dolfin.Form constructor" " will be removed.") self._compiled_form = form._compiled_form self.function_spaces = form.function_spaces self.coefficients = form.coefficients self.subdomains = form.subdomains mesh = form.mesh() else: cpp.dolfin_error("form.py", "creating dolfin.Form", "Expected a ufl.Form or a dolfin.Form") # This is now a strict requirement if mesh is None: raise RuntimeError("Expecting to find a Mesh in the form.") # Type checking argument function_spaces r = self._compiled_form.rank() if len(self.function_spaces) != r: raise ValueError(function_space_error + " Wrong number of test spaces (should be %d)." % r) if not all(isinstance(fs, cpp.FunctionSpace) for fs in self.function_spaces): raise ValueError(function_space_error + " Invalid type of test spaces.") # Type checking coefficients if not all(isinstance(c, cpp.GenericFunction) for c in self.coefficients): coefficient_error = "Error while extracting coefficients. " raise TypeError(coefficient_error + "Either provide a dict of cpp.GenericFunctions, " + "or use Function to define your form.") # Type checking subdomain data # Check that we have no additional subdomain data (user misspelling) # TODO: Get from ufl list? integral_types = ("cell", "exterior_facet", "interior_facet", "point") for k in list(self.subdomains.keys()): if self.subdomains[k] is None: del self.subdomains[k] additional_keys = set(self.subdomains.keys()) - set(integral_types) if additional_keys: cpp.warning("Invalid keys in subdomains: %s" % additional_keys) # Check that we have only MeshFunctions for data in list(self.subdomains.values()): # TODO: This wasn't here before, disable if it's too restrictive: if not (data is None or isinstance(data, MeshFunctionSizet)): cpp.warning("Invalid subdomain data type %s, expecting a MeshFunction" % type(data)) # Initialize base class cpp.Form.__init__(self, self._compiled_form, self.function_spaces, self.coefficients) # Attach subdomains if we have them subdomains = self.subdomains.get("cell") if subdomains is not None: self.set_cell_domains(subdomains) subdomains = self.subdomains.get("exterior_facet") if subdomains is not None: self.set_exterior_facet_domains(subdomains) subdomains = self.subdomains.get("interior_facet") if subdomains is not None: self.set_interior_facet_domains(subdomains) subdomains = self.subdomains.get("point") if subdomains is not None: self.set_vertex_domains(subdomains) # Attach mesh self.set_mesh(mesh)
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 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 assemble_multimesh(form, tensor=None, form_compiler_parameters=None, backend=None): "Assemble the given multimesh form and return the corresponding tensor." # The form that comes in is (by construction in function.Argument) # defined on the first part of the multimesh. We now need to create # the DOLFIN Forms with the proper function spaces for each part. # FIXME: This code makes a number of assumptions and will need to # be revisited and improved. # 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!") form_compiler_parameters = None # Extract arguments and multimesh function space coefficients = form.coefficients() arguments = form.arguments() # Extract rank rank = len(arguments) # Extract multimesh function spaces for arguments V_multi = [v._V_multi for v in arguments] # Extract number of parts, the multimesh and create the multimesh form num_parts = None if rank > 0: num_parts = V_multi[0].num_parts() multimesh_form = cpp.fem.MultiMeshForm(*V_multi) multimesh = V_multi[0].multimesh() elif len(coefficients) > 0: for coeff in coefficients: # Only create these variables once if isinstance(coeff, MultiMeshFunction): multimesh = coeff.function_space().multimesh() num_parts = coeff.function_space().num_parts() multimesh_form = cpp.fem.MultiMeshForm(multimesh) break if not num_parts: # Handle the case Constant(1)*dx(domain=multimesh) multimesh = form.ufl_domains()[0].ufl_cargo() num_parts = multimesh.num_parts() multimesh_form = cpp.fem.MultiMeshForm(multimesh) # Build multimesh DOLFIN form for part in range(num_parts): # Extract standard function spaces for all arguments on # current part function_spaces = [V_multi[i].part(part) for i in range(rank)] # Wrap standard form dolfin_form = _create_dolfin_form(form, form_compiler_parameters, function_spaces) # Setting coefficients for the multimesh form for i in range(len(coefficients)): if isinstance(coefficients[i], MultiMeshFunction): coeff = coefficients[i].part(part) else: coeff = coefficients[i] # Developer note: This may be done more elegantly by modifiying # _create_dolfin_form dolfin_form.set_coefficient(i, coeff._cpp_object) dolfin_form.coefficients[i] = coeff # Add standard mesh to the standard form and the # standard form to the multimesh form dolfin_form.set_mesh(multimesh.part(part)) multimesh_form.add(dolfin_form) for i, coeff in enumerate(coefficients): if isinstance(coeff, MultiMeshFunction): multimesh_form.set_multimesh_coefficient(i, coeff._cpp_object) # Build multimesh form multimesh_form.build() # Create tensor comm = MPI.comm_world tensor = _create_tensor(comm, form, rank, backend, tensor) # Call C++ assemble function assembler = cpp.fem.MultiMeshAssembler() assembler.assemble(tensor, multimesh_form) # Convert to float for scalars if rank == 0: tensor = tensor.get_scalar_value() # Return value return tensor
def down_cast(foo): cpp.warning( "down_cast(foo) is deprecated, please use as_backend_type(foo).") return cpp.as_backend_type(foo)
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)
def mplot_function(ax, f, **kwargs): mesh = f.function_space().mesh() gdim = mesh.geometry().dim() tdim = mesh.topology().dim() # Extract the function vector in a way that also works for # subfunctions try: fvec = f.vector() except RuntimeError: fspace = f.function_space() try: fspace = fspace.collapse() # Happens for part of MultiMeshFunction; no way detecting elsewhere except RuntimeError: cpp.warning("Probably trying to plot MultiMeshFunction " "part. Continuing without plotting...") return fvec = dolfin.interpolate(f, fspace).vector() if fvec.size() == mesh.num_cells(): # DG0 cellwise function C = fvec.array( ) # NB! Assuming here dof ordering matching cell numbering if gdim == 2 and tdim == 2: return ax.tripcolor(mesh2triang(mesh), C, **kwargs) elif gdim == 3 and tdim == 2: # surface in 3d # FIXME: Not tested, probably broken xy = mesh.coordinates() shade = kwargs.pop("shade", True) return ax.plot_trisurf(mesh2triang(mesh), xy[:, 2], C, shade=shade, **kwargs) elif gdim == 1 and tdim == 1: x = mesh.coordinates()[:, 0] nv = len(x) # Insert duplicate points to get piecewise constant plot xp = np.zeros(2 * nv - 2) xp[0] = x[0] xp[-1] = x[-1] xp[1:2 * nv - 3:2] = x[1:-1] xp[2:2 * nv - 2:2] = x[1:-1] Cp = np.zeros(len(xp)) Cp[0:len(Cp) - 1:2] = C Cp[1:len(Cp):2] = C return ax.plot(xp, Cp, *kwargs) #elif tdim == 1: # FIXME: Plot embedded line else: raise AttributeError( 'Matplotlib plotting backend only supports 2D mesh for scalar functions.' ) elif f.value_rank() == 0: # Scalar function, interpolated to vertices # TODO: Handle DG1? C = f.compute_vertex_values(mesh) if gdim == 2 and tdim == 2: mode = kwargs.pop("mode", "contourf") if mode == "contourf": levels = kwargs.pop("levels", 40) return ax.tricontourf(mesh2triang(mesh), C, levels, **kwargs) elif mode == "color": shading = kwargs.pop("shading", "gouraud") return ax.tripcolor(mesh2triang(mesh), C, shading=shading, **kwargs) elif mode == "warp": from matplotlib import cm cmap = kwargs.pop("cmap", cm.jet) linewidths = kwargs.pop("linewidths", 0) return ax.plot_trisurf(mesh2triang(mesh), C, cmap=cmap, linewidths=linewidths, **kwargs) elif mode == "wireframe": return ax.triplot(mesh2triang(mesh), **kwargs) elif mode == "contour": return ax.tricontour(mesh2triang(mesh), C, **kwargs) elif gdim == 3 and tdim == 2: # surface in 3d # FIXME: Not tested from matplotlib import cm cmap = kwargs.pop("cmap", cm.jet) return ax.plot_trisurf(mesh2triang(mesh), C, cmap=cmap, **kwargs) elif gdim == 3 and tdim == 3: # Volume # TODO: Isosurfaces? # Vertex point cloud X = [mesh.coordinates()[:, i] for i in range(gdim)] return ax.scatter(*X, c=C, **kwargs) elif gdim == 1 and tdim == 1: x = mesh.coordinates()[:, 0] ax.set_aspect('auto') # Setting limits for Line2D objects vmin = kwargs.pop("vmin", None) vmax = kwargs.pop("vmax", None) ax.set_ylim([vmin, vmax]) return ax.plot(x, C, **kwargs) #elif tdim == 1: # FIXME: Plot embedded line else: raise AttributeError( 'Matplotlib plotting backend only supports 2D mesh for scalar functions.' ) elif f.value_rank() == 1: # Vector function, interpolated to vertices w0 = f.compute_vertex_values(mesh) nv = mesh.num_vertices() if len(w0) != gdim * nv: raise AttributeError( 'Vector length must match geometric dimension.') X = mesh.coordinates() X = [X[:, i] for i in range(gdim)] U = [w0[i * nv:(i + 1) * nv] for i in range(gdim)] # Compute magnitude C = U[0]**2 for i in range(1, gdim): C += U[i]**2 C = np.sqrt(C) mode = kwargs.pop("mode", "glyphs") if mode == "glyphs": args = X + U + [C] if gdim == 3: # 3d quiver plot works only since matplotlib 1.4 import matplotlib if StrictVersion(matplotlib.__version__) < '1.4': cpp.warning('Matplotlib version %s does not support 3d ' 'quiver plot. Continuing without plotting...' % matplotlib.__version__) return length = kwargs.pop("length", 0.1) return ax.quiver(*args, length=length, **kwargs) else: return ax.quiver(*args, **kwargs) elif mode == "displacement": Xdef = [X[i] + U[i] for i in range(gdim)] import matplotlib.tri as tri if gdim == 2 and tdim == 2: # FIXME: Not tested triang = tri.Triangulation(Xdef[0], Xdef[1], mesh.cells()) shading = kwargs.pop("shading", "flat") return ax.tripcolor(triang, C, shading=shading, **kwargs) else: # Return gracefully to make regression test pass without vtk cpp.warning('Matplotlib plotting backend does not support ' 'displacement for %d in %d. Continuing without ' 'plotting...' % (tdim, gdim)) return
def down_cast(foo): cpp.warning("down_cast(foo) is deprecated, please use as_backend_type(foo).") return cpp.as_backend_type(foo)
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 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 isinstance(uh, MultiMeshFunction): function_space = MultiMeshFunctionSpace vector_space = MultiMeshVectorFunctionSpace tensor_space = MultiMeshTensorFunctionSpace func = MultiMeshFunction else: function_space = FunctionSpace vector_space = VectorFunctionSpace tensor_space = TensorFunctionSpace func = Function if rank == 0: V = function_space(mesh, "Discontinuous Lagrange", degree) elif rank == 1: V = vector_space(mesh, "Discontinuous Lagrange", degree, dim=shape[0]) elif rank > 1: V = tensor_space(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 = func(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 _plot_matplotlib(obj, mesh, kwargs): if not isinstance(obj, _matplotlib_plottable_types): print("Don't know how to plot type %s." % type(obj)) return # Plotting is not working with all ufl cells if mesh.ufl_cell().cellname() not in [ 'interval', 'triangle', 'tetrahedron' ]: raise AttributeError( ("Matplotlib plotting backend doesn't handle %s mesh.\n" "Possible options are saving the output to XDMF file.") % mesh.ufl_cell().cellname()) # Avoid importing pyplot until used try: import matplotlib.pyplot as plt except Exception: cpp.warning("matplotlib.pyplot not available, cannot plot.") return gdim = mesh.geometry.dim if gdim == 3 or kwargs.get("mode") in ("warp", ): # Importing this toolkit has side effects enabling 3d support from mpl_toolkits.mplot3d import axes3d # noqa # Enabling the 3d toolbox requires some additional arguments ax = plt.gca(projection='3d') else: ax = plt.gca() ax.set_aspect('equal') title = kwargs.pop("title", None) if title is not None: ax.set_title(title) # Translate range_min/max kwargs supported by VTKPlotter vmin = kwargs.pop("range_min", None) vmax = kwargs.pop("range_max", None) if vmin and "vmin" not in kwargs: kwargs["vmin"] = vmin if vmax and "vmax" not in kwargs: kwargs["vmax"] = vmax # Drop unsupported kwargs and inform user _unsupported_kwargs = ["rescale", "wireframe"] for kw in _unsupported_kwargs: if kwargs.pop(kw, None): cpp.warning("Matplotlib backend does not support '%s' kwarg yet. " "Ignoring it..." % kw) if isinstance(obj, cpp.function.Function): return mplot_function(ax, obj, **kwargs) # elif isinstance(obj, cpp.function.Expression): # return mplot_expression(ax, obj, mesh, **kwargs) elif isinstance(obj, cpp.mesh.Mesh): return mplot_mesh(ax, obj, **kwargs) elif isinstance(obj, cpp.fem.DirichletBC): return mplot_dirichletbc(ax, obj, **kwargs) elif isinstance(obj, _meshfunction_types): return mplot_meshfunction(ax, obj, **kwargs) else: raise AttributeError('Failed to plot %s' % type(obj))
def plot(object, *args, **kwargs): """ Plot given object. *Arguments* object a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction <dolfin.cpp.MeshFunction>`, a :py:class:`Function <dolfin.functions.function.Function>`, a :py:class:`Expression` <dolfin.cpp.Expression>, a :py:class:`DirichletBC` <dolfin.cpp.DirichletBC> or a :py:class:`FiniteElement <ufl.FiniteElement>`. *Examples of usage* In the simplest case, to plot only e.g. a mesh, simply use .. code-block:: python mesh = UnitSquare(4,4) plot(mesh) Use the ``title`` argument to specify title of the plot .. code-block:: python plot(mesh, tite="Finite element mesh") It is also possible to plot an element .. code-block:: python element = FiniteElement("BDM", tetrahedron, 3) plot(element) Vector valued functions can be visualized with an alternative mode .. code-block:: python plot(u, mode = "glyphs") A more advanced example .. code-block:: python plot(u, wireframe = True, # use wireframe rendering interactive = False, # do not hold plot on screen scalarbar = False, # hide the color mapping bar hardcopy_prefix = "myplot", # default plotfile name scale = 2.0 # scale the warping/glyphs title = "Fancy plot" # Set your own title ) """ mesh = kwargs.get('mesh') p = cpp.Parameters() for key in kwargs: # If there is a "mesh" kwarg it should not be added to the parameters if key != "mesh": try: p.add(key, kwargs[key]) except TypeError: cpp.warning( "Incompatible type for keyword argument \"%s\". Ignoring." % key) # Plot element if isinstance(object, ufl.FiniteElementBase): if os.environ.get("DOLFIN_NOPLOT", "0") != "0": return import ffc return ffc.plot(object, *args, **kwargs) if mesh is None and len(args) == 1 and isinstance(args[0], cpp.Mesh): mesh = args[0] # Plot expression if isinstance(object, cpp.Expression): if mesh is None: raise TypeError, "expected a mesh when plotting an expression." return cpp.plot(object, mesh, p) # Try to project if object is not a standard plottable type if not isinstance( object, (cpp.Function, cpp.Expression, cpp.Mesh, cpp.DirichletBC, cpp.MeshFunction, cpp.MeshFunctionBool, cpp.MeshFunctionInt, cpp.MeshFunctionDouble, cpp.MeshFunctionSizet, cpp.DirichletBC, cpp.CSGGeometry)): from dolfin.fem.projection import project try: cpp.info("Object cannot be plotted directly, projecting to"\ " piecewise linears.") object = project(object, mesh=mesh) except Exception as e: msg = ("Don't know how to plot given object:\n %s\n"\ "and projection failed:\n %s") % (str(object), str(e)) #raise RuntimeError(msg) raise plot_object = cpp.plot(object, p) plot_object.write_ps = _VTKPlotter_write_ps # Avoid premature deletion of plotted objects if they go out of scope # before the plot window is closed. The plotter itself is safe, since it's # created in the plot() C++ function, not directly from Python. But the # Python plotter proxy may disappear, so we can't store the references # there. global _objects_referenced_from_plot_windows _objects_referenced_from_plot_windows[plot_object.key()] = (object, mesh, p) return plot_object
def __init__(self, form, form_compiler_parameters=None): "Create JIT-compiled form from any given form (compiled or not)." # Check form argument if not isinstance(form, ufl.Form): cpp.dolfin_error("form.py", "creating dolfin.Form", "Expected a ufl.Form.") # Cutoff for better error message than ffc would give if not form.empty() and not form.domains(): cpp.dolfin_error("form.py", "creating dolfin.Form", "The ufl.Form does not include any reference to a mesh.") # Extract subdomain data. # Until the assembler and ufc has multimesh support, # we impose the limitation of a single domain at this point. sd = form.subdomain_data() if len(sd) != 1: cpp.dolfin_error("form.py", "creating dolfin.Form", "Expecting subdomain data for a single domain only.") self.subdomains, = list(sd.values()) # Assuming single domain domain, = list(sd.keys()) # Assuming single domain mesh = domain.data() # Having a mesh in the form is now a strict requirement if mesh is None: cpp.dolfin_error("form.py", "creating dolfin.Form", "Expecting to find a Mesh in the form.") # Jit the module, this will take some time... self._compiled_form, module, prefix \ = jit(form, form_compiler_parameters, mpi_comm=mesh.mpi_comm()) # Extract function spaces of form arguments self.function_spaces = [func.function_space() for func in form.arguments()] # Extract coefficients from form (pass only the ones that # the compiled form actually uses to the assembler) original_coefficients = form.coefficients() self.coefficients = [] for i in range(self._compiled_form.num_coefficients()): j = self._compiled_form.original_coefficient_position(i) self.coefficients.append(original_coefficients[j]) # Type checking argument function_spaces r = self._compiled_form.rank() if len(self.function_spaces) != r: raise ValueError(function_space_error + " Wrong number of test spaces (should be %d)." % r) if not all(isinstance(fs, cpp.FunctionSpace) for fs in self.function_spaces): raise ValueError(function_space_error + " Invalid type of test spaces.") # Type checking coefficients if not all(isinstance(c, cpp.GenericFunction) for c in self.coefficients): coefficient_error = "Error while extracting coefficients. " raise TypeError(coefficient_error + "Either provide a dict of cpp.GenericFunctions, " + "or use Function to define your form.") # Initialize base class cpp.Form.__init__(self, self._compiled_form, self.function_spaces, self.coefficients) # Attach mesh (because function spaces and coefficients may be empty lists) self.set_mesh(mesh) # Type checking subdomain data # Delete None entries for k in list(self.subdomains.keys()): if self.subdomains[k] is None: del self.subdomains[k] # Check that we have no additional subdomain data (user misspelling) # TODO: Get from ufl list? integral_types = ("cell", "exterior_facet", "interior_facet", "vertex") additional_keys = set(self.subdomains.keys()) - set(integral_types) if additional_keys: cpp.warning("Invalid keys in subdomains: %s" % additional_keys) # Check that we have only MeshFunctions for data in list(self.subdomains.values()): if not (data is None or isinstance(data, MeshFunctionSizet)): cpp.warning("Invalid subdomain data type %s, expecting a MeshFunction" % type(data)) # Attach subdomains to C++ Form if we have them subdomains = self.subdomains.get("cell") if subdomains is not None: self.set_cell_domains(subdomains) subdomains = self.subdomains.get("exterior_facet") if subdomains is not None: self.set_exterior_facet_domains(subdomains) subdomains = self.subdomains.get("interior_facet") if subdomains is not None: self.set_interior_facet_domains(subdomains) subdomains = self.subdomains.get("vertex") if subdomains is not None: self.set_vertex_domains(subdomains)
def plot(object, *args, **kwargs): """ Plot given object. *Arguments* object a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction <dolfin.cpp.MeshFunction>`, a :py:class:`Function <dolfin.functions.function.Function>`, a :py:class:`Expression` <dolfin.cpp.Expression>, a :py:class:`DirichletBC` <dolfin.cpp.DirichletBC>, a :py:class:`FiniteElement <ufl.FiniteElement>`, or a :py:class:`MultiMesh <dolfin.cpp.MultiMesh>`. *Examples of usage* In the simplest case, to plot only e.g. a mesh, simply use .. code-block:: python mesh = UnitSquare(4,4) plot(mesh) Use the ``title`` argument to specify title of the plot .. code-block:: python plot(mesh, tite="Finite element mesh") It is also possible to plot an element .. code-block:: python element = FiniteElement("BDM", tetrahedron, 3) plot(element) Vector valued functions can be visualized with an alternative mode .. code-block:: python plot(u, mode = "glyphs") A more advanced example .. code-block:: python plot(u, wireframe = True, # use wireframe rendering interactive = False, # do not hold plot on screen scalarbar = False, # hide the color mapping bar hardcopy_prefix = "myplot", # default plotfile name scale = 2.0, # scale the warping/glyphs title = "Fancy plot", # set your own title backend = "vtk" # choose plotting backend ) """ # Plot element if isinstance(object, ufl.FiniteElementBase): if os.environ.get("DOLFIN_NOPLOT", "0") != "0": return import ffc return ffc.plot(object, *args, **kwargs) # Get mesh from explicit mesh kwarg, only positional arg, or via object mesh = kwargs.pop('mesh', None) if isinstance(object, cpp.Mesh): if mesh is not None and mesh.id() != object.id(): cpp.dolfin_error( "plotting.py", "plot mesh", "Got different mesh in plot object and keyword argument") mesh = object if mesh is None: if isinstance(object, cpp.Function): mesh = object.function_space().mesh() elif hasattr(object, "mesh"): mesh = object.mesh() # Expressions do not carry their own mesh if isinstance(object, cpp.Expression) and mesh is None: cpp.dolfin_error("plotting.py", "plot expression", "Expecting a mesh as keyword argument") # Try to project if object is not a standard plottable type if not isinstance(object, _plottable_types): from dolfin.fem.projection import project try: cpp.info("Object cannot be plotted directly, projecting to"\ " piecewise linears.") object = project(object, mesh=mesh) mesh = object.function_space().mesh() except Exception as e: msg = "Don't know how to plot given object:\n %s\n" \ "and projection failed:\n %s" % (str(object), str(e)) cpp.dolfin_error("plotting.py", "plot object", msg) # Select backend backend = kwargs.pop("backend", None) or cpp.parameters["plotting_backend"] if backend == "vtk": return _plot_cpp(object, mesh, kwargs) elif backend == "matplotlib": return _plot_matplotlib(object, mesh, kwargs) elif backend == "none": cpp.warning("Plotting backend set to 'none'. Plotting disabled.") return else: cpp.dolfin_error("plotting.py", "plot object", "Unknown plotting backend '%s'" % backend)
def plot(object, *args, **kwargs): """ Plot given object. *Arguments* object a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction <dolfin.cpp.MeshFunction>`, a :py:class:`Function <dolfin.functions.function.Function>`, a :py:class:`Expression` <dolfin.cpp.Expression>, a :py:class:`DirichletBC` <dolfin.cpp.DirichletBC>, a :py:class:`FiniteElement <ufl.FiniteElement>`, or a :py:class:`MultiMesh <dolfin.cpp.MultiMesh>`. *Examples of usage* In the simplest case, to plot only e.g. a mesh, simply use .. code-block:: python mesh = UnitSquare(4,4) plot(mesh) Use the ``title`` argument to specify title of the plot .. code-block:: python plot(mesh, tite="Finite element mesh") It is also possible to plot an element .. code-block:: python element = FiniteElement("BDM", tetrahedron, 3) plot(element) Vector valued functions can be visualized with an alternative mode .. code-block:: python plot(u, mode = "glyphs") A more advanced example .. code-block:: python plot(u, wireframe = True, # use wireframe rendering interactive = False, # do not hold plot on screen scalarbar = False, # hide the color mapping bar hardcopy_prefix = "myplot", # default plotfile name scale = 2.0, # scale the warping/glyphs title = "Fancy plot", # set your own title backend = "vtk" # choose plotting backend ) """ # Plot element if isinstance(object, ufl.FiniteElementBase): if os.environ.get("DOLFIN_NOPLOT", "0") != "0": return import ffc return ffc.plot(object, *args, **kwargs) # Get mesh from explicit mesh kwarg, only positional arg, or via object mesh = kwargs.pop('mesh', None) if isinstance(object, cpp.Mesh): if mesh is not None and mesh.id() != object.id(): cpp.dolfin_error("plotting.py", "plot mesh", "Got different mesh in plot object and keyword argument") mesh = object if mesh is None: if isinstance(object, cpp.Function): mesh = object.function_space().mesh() elif hasattr(object, "mesh"): mesh = object.mesh() # Expressions do not carry their own mesh if isinstance(object, cpp.Expression) and mesh is None: cpp.dolfin_error("plotting.py", "plot expression", "Expecting a mesh as keyword argument") # Try to project if object is not a standard plottable type if not isinstance(object, _plottable_types): from dolfin.fem.projection import project try: cpp.info("Object cannot be plotted directly, projecting to"\ " piecewise linears.") object = project(object, mesh=mesh) mesh = object.function_space().mesh() except Exception as e: msg = "Don't know how to plot given object:\n %s\n" \ "and projection failed:\n %s" % (str(object), str(e)) cpp.dolfin_error("plotting.py", "plot object", msg) # Select backend backend = kwargs.pop("backend", None) or cpp.parameters["plotting_backend"] if backend == "vtk": return _plot_cpp(object, mesh, kwargs) elif backend == "matplotlib": return _plot_matplotlib(object, mesh, kwargs) elif backend == "none": cpp.warning("Plotting backend set to 'none'. Plotting disabled.") return else: cpp.dolfin_error("plotting.py", "plot object", "Unknown plotting backend '%s'" % backend)
def _down_cast(self): cpp.warning( "foo.down_cast() is deprecated, please use as_backend_type(foo).") return cpp.as_backend_type(self)
def __init__(self, form, form_compiler_parameters=None, function_spaces=None): "Create JIT-compiled form from any given form (compiled or not)." # Developer note: The function_spaces argument is used to # handle forms defined on multimesh function spaces. These are # really defined over a standard function space (the first of # the spaces) then later recreated on each function space part # and added together to form a multimesh form. This happens in # the function assemble_multimesh. # Check form argument if not isinstance(form, ufl.Form): cpp.dolfin_error("form.py", "creating dolfin.Form", "Expected a ufl.Form.") # Cutoff for better error message than ffc would give if not form.empty() and not form.ufl_domains(): cpp.dolfin_error( "form.py", "creating dolfin.Form", "The ufl.Form does not include any reference to a mesh.") # Extract subdomain data. # Until the assembler and ufc has multimesh support, # we impose the limitation of a single domain at this point. sd = form.subdomain_data() if len(sd) != 1: cpp.dolfin_error( "form.py", "creating dolfin.Form", "Expecting subdomain data for a single domain only.") self.subdomains, = list(sd.values()) # Assuming single domain domain, = list(sd.keys()) # Assuming single domain mesh = domain.ufl_cargo() # Having a mesh in the form is now a strict requirement if mesh is None: cpp.dolfin_error("form.py", "creating dolfin.Form", "Expecting to find a Mesh in the form.") # Jit the module, this will take some time... jit_result = jit(form, form_compiler_parameters, mpi_comm=mesh.mpi_comm()) if jit_result is None: cpp.dolfin_error("form.py", "creating dolfin.Form", "jit failure.") self._compiled_form, module, prefix = jit_result # Extract function spaces of form arguments if function_spaces is None: self.function_spaces = [ func.function_space() for func in form.arguments() ] else: self.function_spaces = function_spaces # Type checking argument function_spaces if not all( isinstance(fs, cpp.FunctionSpace) for fs in self.function_spaces): raise ValueError(function_space_error + " Invalid type of test spaces.") # Initialize base class cpp.Form.__init__(self, self._compiled_form, self.function_spaces) # Type checking argument function_spaces #r = self._compiled_form.rank() #if len(self.function_spaces) != r: # raise ValueError(function_space_error + # " Wrong number of test spaces (should be %d)." % r) # Extract coefficients from form (pass only the ones that # the compiled form actually uses to the assembler) original_coefficients = form.coefficients() self.coefficients = [] for i in range(self.num_coefficients()): j = self.original_coefficient_position(i) self.coefficients.append(original_coefficients[j]) # Type checking coefficients if not all( isinstance(c, (cpp.GenericFunction, cpp.MultiMeshFunction)) for c in self.coefficients): # Developer note: # The form accepts a MultiMeshFunction but does not set the # correct coefficients. This is done in assemble_multimesh # at the moment coefficient_error = "Error while extracting coefficients. " raise TypeError(coefficient_error + "Either provide a dict of cpp.GenericFunctions, " + "or use Function to define your form.") for i in range(self.num_coefficients()): if isinstance(self.coefficients[i], cpp.GenericFunction): self.set_coefficient(i, self.coefficients[i]) # Attach mesh (because function spaces and coefficients may be # empty lists) if not function_spaces: self.set_mesh(mesh) # Type checking subdomain data # Delete None entries for k in list(self.subdomains.keys()): if self.subdomains[k] is None: del self.subdomains[k] # Check that we have no additional subdomain data (user # misspelling) # TODO: Get from ufl list? integral_types = ("cell", "exterior_facet", "interior_facet", "vertex") additional_keys = set(self.subdomains.keys()) - set(integral_types) if additional_keys: cpp.warning("Invalid keys in subdomains: %s" % additional_keys) # Check that we have only MeshFunctions for data in list(self.subdomains.values()): if not (data is None or isinstance(data, MeshFunctionSizet)): cpp.warning( "Invalid subdomain data type %s, expecting a MeshFunction" % type(data)) # Attach subdomains to C++ Form if we have them subdomains = self.subdomains.get("cell") if subdomains is not None: self.set_cell_domains(subdomains) subdomains = self.subdomains.get("exterior_facet") if subdomains is not None: self.set_exterior_facet_domains(subdomains) subdomains = self.subdomains.get("interior_facet") if subdomains is not None: self.set_interior_facet_domains(subdomains) subdomains = self.subdomains.get("vertex") if subdomains is not None: self.set_vertex_domains(subdomains)
def _down_cast(self): cpp.warning("foo.down_cast() is deprecated, please use as_backend_type(foo).") return cpp.as_backend_type(self)
def plot(object, *args, **kwargs): """ Plot given object. *Arguments* object a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction <dolfin.cpp.MeshFunction>`, a :py:class:`Function <dolfin.functions.function.Function>`, a :py:class:`Expression` <dolfin.cpp.Expression>, a :py:class:`DirichletBC` <dolfin.cpp.DirichletBC> or a :py:class:`FiniteElement <ufl.FiniteElement>`. *Examples of usage* In the simplest case, to plot only e.g. a mesh, simply use .. code-block:: python mesh = UnitSquare(4,4) plot(mesh) Use the ``title`` argument to specify title of the plot .. code-block:: python plot(mesh, tite="Finite element mesh") It is also possible to plot an element .. code-block:: python element = FiniteElement("BDM", tetrahedron, 3) plot(element) Vector valued functions can be visualized with an alternative mode .. code-block:: python plot(u, mode = "glyphs") A more advanced example .. code-block:: python plot(u, wireframe = True, # use wireframe rendering interactive = False, # do not hold plot on screen scalarbar = False, # hide the color mapping bar hardcopy_prefix = "myplot", # default plotfile name scale = 2.0 # scale the warping/glyphs title = "Fancy plot" # Set your own title ) """ mesh = kwargs.get('mesh') p = cpp.Parameters() for key in kwargs: # If there is a "mesh" kwarg it should not be added to the parameters if key != "mesh": try: p.add(key, kwargs[key]) except TypeError: cpp.warning("Incompatible type for keyword argument \"%s\". Ignoring." % key) # Plot element if isinstance(object, ufl.FiniteElementBase): if os.environ.get("DOLFIN_NOPLOT", "0") != "0": return import ffc return ffc.plot(object, *args, **kwargs) if mesh is None and len(args) == 1 and isinstance(args[0], cpp.Mesh): mesh = args[0] # Plot expression if isinstance(object, cpp.Expression): if mesh is None: raise TypeError, "expected a mesh when plotting an expression." return cpp.plot(object, mesh, p) # Try to project if object is not a standard plottable type if not isinstance(object, (cpp.Function, cpp.Expression, cpp.Mesh, cpp.DirichletBC, cpp.MeshFunction, cpp.MeshFunctionBool, cpp.MeshFunctionInt, cpp.MeshFunctionDouble, cpp.MeshFunctionSizet, cpp.DirichletBC, cpp.CSGGeometry)): from dolfin.fem.projection import project try: cpp.info("Object cannot be plotted directly, projecting to"\ " piecewise linears.") object = project(object, mesh=mesh) except Exception as e: msg = ("Don't know how to plot given object:\n %s\n"\ "and projection failed:\n %s") % (str(object), str(e)) #raise RuntimeError(msg) raise plot_object = cpp.plot(object, p) plot_object.write_ps = _VTKPlotter_write_ps # Avoid premature deletion of plotted objects if they go out of scope # before the plot window is closed. The plotter itself is safe, since it's # created in the plot() C++ function, not directly from Python. But the # Python plotter proxy may disappear, so we can't store the references # there. global _objects_referenced_from_plot_windows _objects_referenced_from_plot_windows[plot_object.key()] = (object, mesh, p) return plot_object
def assemble_multimesh(form, tensor=None, form_compiler_parameters=None, backend=None): "Assemble the given multimesh form and return the corresponding tensor." # The form that comes in is (by construction in function.Argument) # defined on the first part of the multimesh. We now need to create # the DOLFIN Forms with the proper function spaces for each part. # FIXME: This code makes a number of assumptions and will need to # be revisited and improved. # 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!") form_compiler_parameters = None # Extract arguments and multimesh function space coefficients = form.coefficients() arguments = form.arguments() # Extract rank rank = len(arguments) # Extract multimesh function spaces for arguments V_multi = [v._V_multi for v in arguments] # Extract number of parts, the multimesh and create the multimesh form num_parts = None if rank > 0: num_parts = V_multi[0].num_parts() multimesh_form = cpp.fem.MultiMeshForm(*V_multi) multimesh = V_multi[0].multimesh() elif len(coefficients) > 0: for coeff in coefficients: # Only create these variables once if isinstance(coeff, MultiMeshFunction): multimesh = coeff.function_space().multimesh() num_parts = coeff.function_space().num_parts() multimesh_form = cpp.fem.MultiMeshForm(multimesh) break if not num_parts: # Handle the case Constant(1)*dx(domain=multimesh) multimesh = form.ufl_domains()[0].ufl_cargo() num_parts = multimesh.num_parts() multimesh_form = cpp.fem.MultiMeshForm(multimesh) # Build multimesh DOLFIN form for part in range(num_parts): # Extract standard function spaces for all arguments on # current part function_spaces = [V_multi[i].part(part) for i in range(rank)] # Wrap standard form dolfin_form = _create_dolfin_form(form, form_compiler_parameters, function_spaces) # Setting coefficients for the multimesh form for i in range(len(coefficients)): if isinstance(coefficients[i], MultiMeshFunction): coeff = coefficients[i].part(part) else: coeff = coefficients[i] # Developer note: This may be done more elegantly by modifiying # _create_dolfin_form dolfin_form.set_coefficient(i, coeff._cpp_object) dolfin_form.coefficients[i] = coeff # Add standard mesh to the standard form and the # standard form to the multimesh form dolfin_form.set_mesh(multimesh.part(part)) multimesh_form.add(dolfin_form) for i, coeff in enumerate(coefficients): if isinstance(coeff, MultiMeshFunction): multimesh_form.set_multimesh_coefficient(i, coeff._cpp_object) # Build multimesh form multimesh_form.build() # Create tensor comm = MPI.comm_world tensor = _create_tensor(comm, form, rank, backend, tensor) # Call C++ assemble function assembler = cpp.fem.MultiMeshAssembler() assembler.assemble(tensor, multimesh_form) # Convert to float for scalars if rank == 0: tensor = tensor.get_scalar_value() # Return value return tensor