Entity markers#
Author: Jørgen S. Dokken
SPDX-License-Identifier: MIT
DOLFINx gives you full control for marking entities.
However, sometimes this can feel a bit repetative.
In this example we will show how to use scifem.create_entity_markers().
We start by creating a simple unit square
mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, 60, 60)
Next, we want to mark some of the cells in our domain.
We create three marker functions below.
Each of them takes in a (3, num_points) array, and returns a boolean array of size num_points.
def left(x):
return x[0] < 0.2
def right(x):
return x[0] > 0.9
def inner(x):
# We use numpy bit operator `&` for "and"
return (x[0] > 0.3) & (x[0] < 0.7)
We want to mark these entities with with unique integers 1, 3 and 7.
from scifem import create_entity_markers
cell_tag = create_entity_markers(mesh, mesh.topology.dim, [(1, left), (3, right), (7, inner)])
Next we can plot these marked entities
import pyvista
vtk_grid = dolfinx.plot.vtk_mesh(mesh, cell_tag.dim, cell_tag.indices)
grid = pyvista.UnstructuredGrid(*vtk_grid)
grid.cell_data["Marker"] = cell_tag.values
Create plotter
plotter = pyvista.Plotter()
plotter.add_mesh(grid)
plotter.view_xy()
if not pyvista.OFF_SCREEN:
plotter.show()
2026-08-05 06:37:28.112 ( 0.531s) [ 7FEF59FCF140]vtkXOpenGLRenderWindow.:1460 WARN| bad X server connection. DISPLAY=:99.0
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /dolfinx-env/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py:546, in StaticResource.__init__(self, prefix, directory, name, expect_handler, chunk_size, show_index, follow_symlinks, append_version)
545 try:
--> 546 directory = Path(directory).expanduser().resolve(strict=True)
547 except FileNotFoundError as error:
File /usr/lib/python3.12/pathlib.py:1242, in Path.resolve(self, strict)
1241 try:
-> 1242 s = self._flavour.realpath(self, strict=strict)
1243 except OSError as e:
File <frozen posixpath>:432, in realpath(filename, strict)
File <frozen posixpath>:477, in _joinrealpath(path, rest, strict, seen)
FileNotFoundError: [Errno 2] No such file or directory: '/dolfinx-env/lib/python3.12/site-packages/trame_client/module/vue3-www'
The above exception was the direct cause of the following exception:
ValueError Traceback (most recent call last)
Cell In[10], line 5
1 plotter = pyvista.Plotter()
2 plotter.add_mesh(grid)
3 plotter.view_xy()
4 if not pyvista.OFF_SCREEN:
----> 5 plotter.show()
File /dolfinx-env/lib/python3.12/site-packages/pyvista/_deprecate_positional_args.py:243, in _deprecate_positional_args.<locals>._inner_deprecate_positional_args.<locals>.inner_f(*args, **kwargs)
239 warn_external(msg, PyVistaDeprecationWarning)
241 warn_positional_args()
--> 243 return f(*args, **kwargs)
File /dolfinx-env/lib/python3.12/site-packages/pyvista/plotting/plotter.py:8231, in Plotter.show(self, title, window_size, interactive, auto_close, interactive_update, full_screen, screenshot, return_img, cpos, jupyter_backend, return_viewer, return_cpos, before_close_callback, store_image_depth, **kwargs)
8228 jupyter_backend = self._theme.jupyter_backend
8230 if jupyter_backend is None or jupyter_backend.lower() != 'none':
-> 8231 jupyter_disp = handle_plotter(self, backend=jupyter_backend, **jupyter_kwargs)
8233 self.render()
8235 # initial double render needed for certain passes when offscreen
File /dolfinx-env/lib/python3.12/site-packages/pyvista/jupyter/notebook.py:74, in handle_plotter(plotter, backend, screenshot, **kwargs)
71 if backend in ['server', 'client', 'trame', 'html']:
72 from pyvista.trame.jupyter import show_trame # noqa: PLC0415
---> 74 return show_trame(plotter, mode=backend, **kwargs)
76 except ImportError as e:
77 # Trame was explicitly requested but not available
78 _ensure_entry_points()
File /dolfinx-env/lib/python3.12/site-packages/pyvista/trame/jupyter.py:394, in show_trame(plotter, mode, name, server_proxy_enabled, server_proxy_prefix, jupyter_extension_enabled, collapse_menu, add_menu, add_menu_items, default_server_rendering, handler, **kwargs)
391 kwargs.setdefault('height', dh)
393 if mode == 'html':
--> 394 return EmbeddableWidget(plotter, **kwargs)
396 if jupyter_extension_enabled is None:
397 jupyter_extension_enabled = pv.global_theme.trame.jupyter_extension_enabled
File /dolfinx-env/lib/python3.12/site-packages/pyvista/trame/jupyter.py:137, in EmbeddableWidget.__init__(self, plotter, width, height, **kwargs)
135 msg = 'Please install `ipywidgets`.'
136 raise ImportError(msg)
--> 137 scene = plotter.export_html(filename=None)
138 src = scene.getvalue().replace('"', '"')
139 # eventually we could maybe expose this, but for now make sure we're at least
140 # consistent with matplotlib's color (light gray)
File /dolfinx-env/lib/python3.12/site-packages/pyvista/plotting/plotter.py:784, in BasePlotter.export_html(self, filename)
781 msg = 'Please install trame dependencies: pip install "pyvista[jupyter]"'
782 raise ImportError(msg)
--> 784 data = self.export_vtksz(filename=None)
785 buffer = io.StringIO()
786 write_html(data, buffer)
File /dolfinx-env/lib/python3.12/site-packages/pyvista/plotting/plotter.py:837, in BasePlotter.export_vtksz(self, filename, format)
835 server = get_server(pv.global_theme.trame.jupyter_server_name)
836 if not server.running:
--> 837 elegantly_launch(pv.global_theme.trame.jupyter_server_name)
839 view = PyVistaLocalView(self, trame_server=server)
841 content = view.export(format=format)
File /dolfinx-env/lib/python3.12/site-packages/pyvista/trame/jupyter.py:487, in elegantly_launch(*args, **kwargs)
484 # Basically monkey patches asyncio to support this
485 nest_asyncio2.apply()
--> 487 return asyncio.run(launch_it())
File /dolfinx-env/lib/python3.12/site-packages/nest_asyncio2.py:115, in run(main, debug, loop_factory)
113 task = asyncio.ensure_future(main, loop=loop)
114 try:
--> 115 return loop.run_until_complete(task)
116 finally:
117 if not task.done():
File /dolfinx-env/lib/python3.12/site-packages/nest_asyncio2.py:230, in _patch_loop.<locals>.run_until_complete(self, future)
227 if self._ready:
228 self._run_once()
--> 230 return f.result()
File /usr/lib/python3.12/asyncio/futures.py:203, in Future.result(self)
201 self.__log_traceback = False
202 if self._exception is not None:
--> 203 raise self._exception.with_traceback(self._exception_tb)
204 return self._result
File /usr/lib/python3.12/asyncio/tasks.py:314, in Task.__step_run_and_handle_result(***failed resolving arguments***)
310 try:
311 if exc is None:
312 # We use the `send` method directly, because coroutines
313 # don't have `__iter__` and `__next__` methods.
--> 314 result = coro.send(None)
315 else:
316 result = coro.throw(exc)
File /dolfinx-env/lib/python3.12/site-packages/pyvista/trame/jupyter.py:482, in elegantly_launch.<locals>.launch_it()
481 async def launch_it():
--> 482 await launch_server(*args, **kwargs).ready
File /dolfinx-env/lib/python3.12/site-packages/pyvista/trame/jupyter.py:215, in launch_server(server, port, host, wslink_backend, **kwargs)
213 if server._running_stage == 0:
214 server.controller.on_server_ready.add(on_ready)
--> 215 server.start(
216 exec_mode='task',
217 host=host,
218 port=port,
219 open_browser=False,
220 show_connection_info=False,
221 disable_logging=True,
222 timeout=0,
223 backend=wslink_backend,
224 )
225 # else, server is already running or launching
226 return server
File /dolfinx-env/lib/python3.12/site-packages/trame_server/core.py:725, in Server.start(self, port, thread, open_browser, show_connection_info, disable_logging, backend, follow_symlinks, exec_mode, timeout, host, **kwargs)
722 CoreServer.configure(options)
724 self._running_stage = 1
--> 725 task = CoreServer.server_start(
726 options,
727 **{ # Do a proper merging/override
728 **kwargs,
729 "disableLogging": disable_logging,
730 "backend": backend,
731 "exec_mode": exec_mode,
732 },
733 )
735 # Manage exit life cycle unless coroutine
736 if exec_mode == "main":
File /dolfinx-env/lib/python3.12/site-packages/trame_server/protocol.py:52, in CoreServer.server_start(options, disableLogging, backend, exec_mode, **kwargs)
43 @staticmethod
44 def server_start(
45 options,
(...) 50 ):
51 # NOTE: **kwargs to wslink's start_webserver are currently unused
---> 52 return server.start_webserver(
53 options=options,
54 protocol=CoreServer,
55 disableLogging=disableLogging,
56 backend=backend,
57 exec_mode=exec_mode,
58 **kwargs,
59 )
File /dolfinx-env/lib/python3.12/site-packages/wslink/server.py:257, in start_webserver(options, protocol, disableLogging, backend, exec_mode, **_)
254 server_config["handle_signals"] = not options.nosignalhandlers
256 # Create the webserver and start it
--> 257 ws_server = create_webserver(server_config, backend=backend)
259 # Once we have python 3.7 minimum, we can start the server with asyncio.run()
260 # asyncio.run(ws_server.start())
261
262 # Until then, we can start the server this way
263 try:
File /dolfinx-env/lib/python3.12/site-packages/wslink/server.py:168, in create_webserver(server_config, backend)
167 def create_webserver(server_config, backend="aiohttp"):
--> 168 return backends.create_webserver(server_config, backend=backend)
File /dolfinx-env/lib/python3.12/site-packages/wslink/backends/__init__.py:5, in create_webserver(server_config, backend)
2 if backend == "aiohttp":
3 from .aiohttp import create_webserver # noqa: PLC0415
----> 5 return create_webserver(server_config)
7 if backend == "generic":
8 from .generic import create_webserver # noqa: PLC0415
File /dolfinx-env/lib/python3.12/site-packages/wslink/backends/aiohttp/__init__.py:218, in create_webserver(server_config)
215 return ReverseWebAppServer(server_config)
217 # Normal web server
--> 218 return WebAppServer(server_config)
File /dolfinx-env/lib/python3.12/site-packages/wslink/backends/aiohttp/__init__.py:118, in WebAppServer.__init__(self, server_config)
116 # Resolve / => index.html
117 self.app.router.add_route("GET", "/", _root_handler)
--> 118 self.app.add_routes(routes)
120 self.app[STATE_KEY] = {}
File /dolfinx-env/lib/python3.12/site-packages/aiohttp/web_app.py:379, in Application.add_routes(self, routes)
378 def add_routes(self, routes: Iterable[AbstractRouteDef]) -> list[AbstractRoute]:
--> 379 return self.router.add_routes(routes)
File /dolfinx-env/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py:1259, in UrlDispatcher.add_routes(self, routes)
1257 registered_routes = []
1258 for route_def in routes:
-> 1259 registered_routes.extend(route_def.register(self))
1260 return registered_routes
File /dolfinx-env/lib/python3.12/site-packages/aiohttp/web_routedef.py:87, in StaticDef.register(self, router)
86 def register(self, router: UrlDispatcher) -> list[AbstractRoute]:
---> 87 resource = router.add_static(self.prefix, self.path, **self.kwargs)
88 routes = resource.get_info().get("routes", {})
89 return list(routes.values())
File /dolfinx-env/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py:1183, in UrlDispatcher.add_static(self, prefix, path, name, expect_handler, chunk_size, show_index, follow_symlinks, append_version)
1181 if prefix.endswith("/"):
1182 prefix = prefix[:-1]
-> 1183 resource = StaticResource(
1184 prefix,
1185 path,
1186 name=name,
1187 expect_handler=expect_handler,
1188 chunk_size=chunk_size,
1189 show_index=show_index,
1190 follow_symlinks=follow_symlinks,
1191 append_version=append_version,
1192 )
1193 self.register_resource(resource)
1194 return resource
File /dolfinx-env/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py:548, in StaticResource.__init__(self, prefix, directory, name, expect_handler, chunk_size, show_index, follow_symlinks, append_version)
546 directory = Path(directory).expanduser().resolve(strict=True)
547 except FileNotFoundError as error:
--> 548 raise ValueError(f"'{directory}' does not exist") from error
549 if not directory.is_dir():
550 raise ValueError(f"'{directory}' is not a directory")
ValueError: '/dolfinx-env/lib/python3.12/site-packages/trame_client/module/vue3-www' does not exist
We can also mark lower order entities, such as facets
def circle(x):
return x[0] ** 2 + x[1] ** 2 <= 0.16**2
def top(x):
return x[1] > 0.9
facet_tags = create_entity_markers(mesh, mesh.topology.dim - 1, [(2, top), (7, circle)])
facet_grid = dolfinx.plot.vtk_mesh(mesh, facet_tags.dim, facet_tags.indices)
fgrid = pyvista.UnstructuredGrid(*facet_grid)
fgrid.cell_data["Marker"] = facet_tags.values
fplotter = pyvista.Plotter()
fplotter.add_mesh(fgrid)
fplotter.view_xy()
if not pyvista.OFF_SCREEN:
fplotter.show()
We can also exclude interior facets by adding on_boundary: True (by default this is set to False).
boundary_facet_tags = create_entity_markers(
mesh, mesh.topology.dim - 1, [(2, top, True), (7, circle, False)]
)
boundary_grid = dolfinx.plot.vtk_mesh(mesh, boundary_facet_tags.dim, boundary_facet_tags.indices)
bfgrid = pyvista.UnstructuredGrid(*boundary_grid)
bfgrid.cell_data["Marker"] = boundary_facet_tags.values
bfplotter = pyvista.Plotter()
bfplotter.add_mesh(bfgrid)
bfplotter.view_xy()
if not pyvista.OFF_SCREEN:
bfplotter.show()