API reference#
Every public function is also importable from the top-level fenicsx_compat package.
Version checks#
Meshes#
- fenicsx_compat.mesh.cmap(mesh: Mesh) CoordinateElement[source]#
Get the coordinate element for a mesh’s geometry.
dolfinx 0.10 has only the
geometry.cmapproperty. From 0.11,geometry.cmapsexists andcmapemits a deprecation warning, so prefercmaps[0]where it exists. Dispatches onhasattr.
- fenicsx_compat.mesh.dofmap(mesh: Mesh) ndarray[tuple[Any, ...], dtype[int32]][source]#
Get the geometry dofmap for a mesh, across the same
dofmapssplit ascmap.
- fenicsx_compat.mesh.form_map(form: Form) tuple[IndexMap, int][source]#
Get the index map and block size for a linear form’s test space.
FunctionSpace.dofmapsis a method on dolfinx 0.10 and a sequence from 0.11. Dispatches on theTypeErrorraised by calling it.For a mesh with a single cell type,
form.function_spaces[0].dofmapgives the same on every version without this function.
- fenicsx_compat.mesh.meshtags_topology(tags: MeshTags) Topology[source]#
Get the topology a MeshTags is defined on, as the Python
dolfinx.mesh.Topology.dolfinx 0.10 and 0.11 return the cpp topology from
MeshTags.topology; dolfinx PR #4350 (post-0.11) returns the Python wrapper. The cpp object is the wrapper’s_cpp_objecton every version. Dispatches onisinstance.
- fenicsx_compat.mesh.cell_permutation_info(topology: Topology) ndarray[tuple[Any, ...], dtype[uint32]][source]#
Compute and return the packed per-cell permutation info, ghost cells included.
dolfinx 0.10 and 0.11 compute it, together with the facet permutations, in a single argument-free
topology.create_entity_permutations(). dolfinx PR #3904 (post-0.11) split that intocreate_cell_permutations()and a per-dimensioncreate_entity_permutations(dim). Dispatches onhasattr.
- fenicsx_compat.mesh.facet_permutations(topology: Topology) ndarray[tuple[Any, ...], dtype[uint8]][source]#
Compute and return the permutation of every facet of every cell.
Shape
(num_cells, num_facets_per_cell), ghost cells included. Each value encodes the facet’s orientation as seen from the cell, as used for FFCx’squadrature_permutation.Across the same dolfinx PR #3904 split as
cell_permutation_info: 0.10 and 0.11 expose these viaget_facet_permutations()after an argument-freecreate_entity_permutations(); from #3904 they areget_entity_permutations(tdim - 1)aftercreate_entity_permutations(tdim - 1). Both return a flat array.
- fenicsx_compat.mesh.create_facet_orientations(topology: Topology) None[source]#
Compute the facet orientations that contravariant Piola spaces on a manifold need.
dolfinx PR #4625 (post-0.11) refuses to create a Raviart-Thomas or other contravariant Piola space on a manifold until
topology.create_facet_orientations()has been called, and that method does not exist on 0.10 and 0.11, which create such spaces without it. Calls it where it exists and does nothing otherwise. Dispatches onhasattr.Call it before creating the space, on every rank: it is collective, and changing the orientations afterwards invalidates functions on existing spaces. Where it exists it raises
ValueErrorunless the topological dimension is 2, as the native method does.
- fenicsx_compat.mesh.create_cell_partitioner(ghost_mode: GhostMode, max_facet_to_cell_links: int = 2) Callable[source]#
Build a cell partitioner with ghost mode baked in, for dolfinx 0.10 and 0.11.
Not needed (and not called) on dolfinx generations where
create_cell_partitionerwas removed (post dolfinx PR #4403) —create_meshtakesghost_modedirectly on those. RaisesNotImplementedErrorif called anyway, so a caller doesn’t silently get an un-ghosted mesh.
- fenicsx_compat.mesh.create_mesh(comm: Comm, cells: ndarray[tuple[Any, ...], dtype[int64]], e, x: ndarray[tuple[Any, ...], dtype[floating]], *, partitioner: Callable | None = None, ghost_mode: GhostMode = GhostMode.shared_facet, max_facet_to_cell_links: int = 2, num_threads: int = 1) Mesh[source]#
Create a mesh from topology and geometry arrays, with a given ghost mode.
Post dolfinx PR #4403 (post-0.11),
ghost_modeandnum_threadsarecreate_mesh’s own kwargs andcreate_cell_partitioneris gone. On 0.10 and 0.11 the ghost mode must be baked into the partitioner viacreate_cell_partitioner;num_threadsis ignored there. Dispatches oninspect.signature.If
partitioneris given explicitly it is always used as-is (never silently replaced);ghost_modeis still passed tocreate_meshon generations that take it.The default
ghost_modeisshared_facet, while dolfinx’s own default isnoneon every version.
- fenicsx_compat.mesh.reconstruct_mesh(mesh: Mesh, coordinate_element_degree: int) Mesh[source]#
Copy a mesh, changing its coordinate element degree.
The topology is shared with the original mesh; the geometry is reconstructed. Delegates to the native
dolfinx.fem.interpolate_geometryfrom dolfinx>=0.11; for 0.10, reconstructs the geometry manually (no native equivalent exists at that version).
- fenicsx_compat.mesh.transfer_meshtags_to_submesh(entity_tag: MeshTags, submesh: Mesh, cell_to_parent, vertex_to_parent) MeshTags[source]#
Transfer mesh tags from a parent mesh to a submesh.
Native since dolfinx 0.11.0 (dolfinx PR #4172, merged 2026-04-23, confirmed absent at the v0.10.0 tag). Raises NotImplementedError on 0.10 — scifem’s own pre-0.11 fallback calls a compiled
_scifemnanobind extension, and fenicsx-compat ships no compiled extensions (see fenicsx-compat design spec §2, §9 item 1).IMPORTANT: native dolfinx’s own positional argument order for
cell_to_parent/vertex_to_parentis NOT stable across the versions this wrapper supports. v0.11.0’s signature is(entity_tag, submesh, vertex_to_parent, cell_to_parent); nightly (0.12.0.dev0) swapped the two to(entity_tag, submesh, cell_to_parent, vertex_to_parent). The parameter names are identical across the swap, so this wrapper forwards to native by keyword, never positionally — that is correct on both versions regardless of which one is installed. Forwarding positionally in nightly’s order (as an earlier revision of this function did) is silently wrong on 0.11: it hands native the cell map where it expects the vertex map and vice versa, which does not raise a Python exception but corrupts internal state and crashes the process with SIGTERM (observed on this project’s stable/0.11 CI leg). Do not “simplify” this back to a positional call.This wrapper’s own declared parameter order,
(entity_tag, submesh, cell_to_parent, vertex_to_parent), is fixed and does not change with the installed dolfinx version — chosen to match nightly’s order andcreate_submesh’s(entity/cell map, vertex map)return order, so callers can unpackcreate_submesh’s output and forward it directly.
Finite elements and interpolation#
- fenicsx_compat.fem.real_functionspace(mesh: Mesh, value_shape: tuple[int, ...] = ()) FunctionSpace[source]#
Create a real (single-constant-per-domain) function space.
This is a deliberate exception to this project’s prefer-introspection rule (spec §4):
basix.ufl.real_elementexists and constructs successfully on both sides of the dolfinx 0.10/0.11 boundary, sohasattrcannot distinguish them. What actually differs is deeper: at dolfinx v0.10.0 the elementreal_elementreturns is a non-functional stub whosebasix_elementproperty raises a bare, message-lessNotImplementedError(confirmed by the v0.10.0 CI traceback:dolfinx.fem.functionspaceaccessesufl_e.basix_elementwhile building the space, which is exactly where it dies). From dolfinx 0.11 that property works anddolfinx.fem.functionspacesucceeds (confirmed locally on this install, dolfinx 0.12.0.dev0). Since nohasattr/signature probe can see into that property without constructing a whole function space, we gate onat_least("0.11.0")instead - a hard version check, used here only because introspection genuinely cannot distinguish the behaviour. Do not “fix” this back to an introspection-only check.Separately, and unrelated to the above,
real_elementgained adtypeparameter in basix 0.11 (basix PR #1001). That part is detected byinspect.signature, anddtype=is only passed when the installed signature has that parameter.The
hasattr(basix.ufl, "real_element")guard below covers a different failure mode:basixis a separate, independently installable package fromdolfinx, so it can be version-skewed against it. A clear NotImplementedError beats an AttributeError deeper in the call.
- fenicsx_compat.fem.finite_element_ctor_kwargs(constructor, basix_element, *, gdim: int, value_shape: tuple[int, ...] | None, symmetric: bool = False)[source]#
Construct a single-element cpp FiniteElement, across the
gdimaddition.dolfinx PR #4511 (post-0.11) added the mesh’s geometric dimension as a required constructor argument, needed for elements on manifolds. 0.10 and 0.11 take
(element, block_shape, symmetric)only, andgdimis ignored there. Dispatches on theTypeErrornanobind raises for the unmatched overload, before any work is done.constructorisdolfinx.cpp.fem.FiniteElement_float32or_float64;value_shapeis passed as dolfinx’sblock_shape(Nonefor an unblocked element).
- fenicsx_compat.fem.interpolate(cpp_function, values: ndarray[tuple[Any, ...], dtype[_ScalarT]], cells: ndarray[tuple[Any, ...], dtype[int32]]) None[source]#
Interpolate raw values into a cpp Function, across the interpolate_f/interpolate rename.
- fenicsx_compat.fem.expression_eval(expr, domain, entity: ndarray[tuple[Any, ...], dtype[int32]]) ndarray[tuple[Any, ...], dtype[_ScalarT]][source]#
Evaluate an Expression at integration entities.
Facet entities are a 2D
[[cell, local_entity]]array from dolfinx PR #3625, which is in 0.10, soexpr.eval(domain, entity)works the same on every supported version. The retry with the entities flattened is for the 1D shape of older dolfinx.
- fenicsx_compat.fem.permute_facet_quadrature(facet_type: CellType, points: ndarray[tuple[Any, ...], dtype[floating]]) list[ndarray[tuple[Any, ...], dtype[floating]]][source]#
Build the per-permutation facet quadrature point table.
Entry
pholds the points to use for a facet with permutationp, so that an Expression evaluated on a facet is evaluated atpointsas seen from the cell. Before dolfinx PR #4140 (dolfinx<0.11.0.dev0), facet Expressions are not permuted at all, so every entry ispoints. From 0.11.0.dev0, dolfinx and FFCx (FFCx PR #836) map the points through the facet’s global orientation, so each entry ispointswith the inverse of that permutation applied, computed withffcx.ir.elementtables(an internal, non-public FFCx module).
- fenicsx_compat.fem.permute_interpolation_data(data: ndarray[tuple[Any, ...], dtype[_ScalarT]], integration_entities: ndarray[tuple[Any, ...], dtype[int32]], cell_permutation_info: ndarray[tuple[Any, ...], dtype[uint32]], basix_element, facet_type: CellType) None[source]#
In-place permute interpolation data for the pre-dolfinx-PR-#4140 dof ordering.
No-op from dolfinx>=0.11.0.dev0, where dolfinx applies this permutation internally.
- fenicsx_compat.fem.interpolate_to_submesh_entity_maps(volume_function: Function, points: ndarray[tuple[Any, ...], dtype[floating]], entity_maps=None) Expression[source]#
Build an Expression, across the
entity_maps-kwarg-existence split onExpression.entity_mapsis passed from dolfinx PR #4140 (0.11) on. dolfinx 0.10 has no such argument, so there it is dropped.
Index maps and vectors#
- fenicsx_compat.la.create_index_map(comm, num_local: int, ghosts: ndarray[tuple[Any, ...], dtype[int64]], owners: ndarray[tuple[Any, ...], dtype[int32]], tag: int = 0) IndexMap[source]#
Build an IndexMap, across the class-constructor-to-factory-function rename.
dolfinx PR #4496 (post-0.11) replaced the
dolfinx.common.IndexMapclass constructor with adolfinx.common.index_mapfactory taking ghosts as a single(ghosts, owners)tuple kwarg, and returning the new PythonIndexMapinstead of the cpp one. Thetagargument (used internally for MPI communication) is caller-specific — pass a value unique across index maps that may communicate concurrently.
- fenicsx_compat.la.unwrap_index_map(index_map) IndexMap[source]#
Return the underlying cpp IndexMap, whether given the Python wrapper or the cpp object itself.
- fenicsx_compat.la.index_to_dest_ranks(index_map, tag: int) tuple[ndarray[tuple[Any, ...], dtype[int32]], ndarray[tuple[Any, ...], dtype[int32]]][source]#
The ranks sharing each local index of
index_map, as(ranks, offsets).Ranks sharing local index
iareranks[offsets[i]:offsets[i + 1]]: for an owned index, the ranks that ghost it; for a ghost, its owner and the other ranks that ghost it. The calling rank is excluded. Collective.dolfinx 0.10 and 0.11 take an MPI
tagfor the consensus exchange and return anAdjacencyList. dolfinx PR #4354 (post-0.11) made it return a(ranks, offsets)tuple, and #4573 dropped the tag.tagis only used on the older versions; pass a value no other exchange in flight uses. Accepts either the Python wrapper or the cppIndexMap. Dispatches on theTypeErrorraised while binding the arguments, which happens on every rank before any communication, so it cannot deadlock.
- fenicsx_compat.la.vector(index_map, bs: int, dtype: type[~typing.Any] | ~numpy.dtype[~typing.Any] | ~numpy._typing._dtype_like._SupportsDType[~numpy.dtype[~typing.Any]] | tuple[~typing.Any, ~typing.Any] | list[~typing.Any] | ~numpy._typing._dtype_like._DTypeDict | str | None = <class 'numpy.float64'>) Vector[source]#
Create a distributed vector compatible with an index map and block size.
dtypemust stay keyword — it became keyword-only from dolfinx 0.12 (dolfinx PR #4489). Passindex_mapas dolfinx returns it: from dolfinx PR #4496dolfinx.la.vectorneeds the PythonIndexMap, and a cpp one raisesAttributeError. Never passscatterer— dolfinx 0.11 has no such parameter.
- fenicsx_compat.la.create_sub_index_map(index_map, indices: ndarray[tuple[Any, ...], dtype[int32]], *, allow_owner_change: bool = False) tuple[IndexMap, ndarray[tuple[Any, ...], dtype[int32]]][source]#
Build an index map for a subset of the local indices of
index_map.Returns
(sub_map, sub_to_parent): the sub-map, and the local index inindex_mapof each of its local indices.indicesmust be sorted and unique, as dolfinx 0.10 and 0.11 require. An index included by a rank that ghosts it but not by its owner has to change owner; unlessallow_owner_change, that raisesRuntimeError. Collective.dolfinx 0.10 and 0.11 have this only as
dolfinx.cpp.common.create_sub_index_map(map, indices, allow_owner_change), which throws on the owning rank alone. dolfinx PR #4479 (post-0.11) dropped the flag in favour of always allowing the change and returning a rank-local flag saying whether one happened, and #4496 added the Pythondolfinx.common.create_sub_index_map. There the flag is reduced here before raising, so every rank raises together. Accepts either the Python wrapper or the cppIndexMap;sub_mapis whichever type the installed dolfinx returns. Dispatches onhasattr.
- fenicsx_compat.la.local_range(index: int, N: int, size: int) tuple[int, int][source]#
The half-open range of
[0, N)held by blockindexofsizealmost-equal blocks.The first
N % sizeblocks hold one index more than the rest, the split dolfinx itself uses to distribute indices over ranks. dolfinx 0.10 and 0.11 do not expose the free functiondolfinx.common.local_rangeto Python (only theIndexMapproperty of the same name); it is reproduced here where it is missing. Dispatches onhasattr.
PETSc#
- fenicsx_compat.petsc.ghost_update(x: Vec, insert_mode, scatter_mode) None[source]#
Ghost-update a PETSc vector.
- fenicsx_compat.petsc.set_bc(b: Vec, bcs, x0=None, alpha: float = 1.0) None[source]#
Set boundary condition values in a vector, across the nested-vs-single-form
set_bcAPI.
- fenicsx_compat.petsc.apply_lifting_and_set_bc(b: Vec, a, bcs, x: Vec | None = None, alpha: float = 1.0) None[source]#
Apply lifting to a vector and set boundary conditions.
Convenience function to apply lifting and set boundary conditions for single, blocked, or nested forms. Modifies
bsuch that its free rows becomeb - alpha * A (g - x)and its constrained rowsalpha * (g - x), withgthe boundary values andAthe operator ofa.- Parameters:
b – The vector to apply lifting to.
a – Form or nested sequence of forms to apply lifting from.
bcs – The boundary conditions to apply.
x – Vector to subtract from the boundary conditions (e.g. in a Newton iteration).
alpha – Scaling factor for the boundary conditions.
- fenicsx_compat.petsc.bcs_by_block(spaces: Sequence[FunctionSpace | None], bcs: Sequence[DirichletBC]) list[list[DirichletBC]][source]#
Arrange boundary conditions by the space they constrain, across dolfinx 0.11/0.12.
dolfinx.fem.bcs_by_blockonly works when the spaces andbc.function_spaceare the same flavour: both cpp objects on 0.10 and 0.11, both Python wrappers from dolfinx PRs #4342 and #4350. That holds for spaces fromdolfinx.fem.extract_function_spaces, but not for spaces the caller builds a list of. Normalizing both sides to cpp objects and calling the cppcontainsdirectly accepts either flavour on every version.
UFL#
- fenicsx_compat.ufl.domain_of(expr)[source]#
Get the single UFL domain an expression is defined on.
ufl.domain.extract_domainsis available across the whole 0.10+ floor; theexpr.ufl_domain()fallback found duplicated across the source repos (some with a fallback, some without — see spec §5.6) is below-floor legacy and is not ported.
- fenicsx_compat.ufl.apply_pullback_inverse(pullback, expr, domain)[source]#
Map
exprfrom the physical cell to the reference cell.Uses
pullback.apply_inverse, added in FEniCS/ufl PR #511 (first released in UFL 2026.2.0), when the installed UFL has it. The UFL releases dolfinx 0.10 and 0.11 pin (2025.2, 2026.1) lack it, so a closed form is used for the identity, contravariant Piola and covariant Piola pullbacks; any other pullback raisesNotImplementedErrorthere. Dispatches onhasattr.
Input and output#
- fenicsx_compat.io.vtk_cell_type(cell_type: CellType, num_nodes: int) int[source]#
The VTK cell type identifier of a cell with
num_nodesgeometry nodes.dolfinx 0.10 and 0.11 have
dolfinx.cpp.io.get_vtk_cell_type(cell, dim), which returns the identifier of the dimension-dimsub-entity ofcell. dolfinx PR #4621 (post-0.11) replaceddimwith the number of nodes, so that it can tell the serendipity layouts apart and reject node counts no Lagrange layout has; passing the topological dimension there raises for quadrilaterals and hexahedra. For a Lagrange layout both return the same arbitrary-degree Lagrange identifier. On 0.10 and 0.11num_nodesis not checked, and the whole cell’s identifier is returned.Neither the argument count nor
inspect.signaturetells the two apart, so this dispatches on the parameter name in the nanobind signature, i.e. the first line of the docstring.
- fenicsx_compat.io.resolve_adios_scope(adios2)[source]#
Resolve the ADIOS2 Python module scope, across the
adios2.bindingssubmodule split.
- fenicsx_compat.io.pyvista_allow_snake_case(pyvista) None[source]#
Allow snake_case VTK attribute access.
Across pyvista’s
_VTK_SNAKE_CASE_STATErename (pyvista 0.47+).
- fenicsx_compat.io.model_to_mesh(model, comm, rank: int, gdim: int = 3, *, partitioner=None, ghost_mode: ~dolfinx.cpp.mesh.GhostMode = GhostMode.shared_facet, max_facet_to_cell_links: int = 2, dtype=<class 'numpy.float64'>)[source]#
Create a mesh from a gmsh model, with a given ghost mode.
Returns the
dolfinx.io.gmsh.MeshDatathat dolfinx builds. Across the same dolfinx PR #4403 split ascreate_mesh: from there,model_to_meshtakesghost_modeand threads it into whichever partitioner is used. On 0.10 and 0.11 the ghost mode can only reach the mesh through a partitioner built with it, so one is built here whenpartitioneris not given. dolfinx 0.10 also has nomax_facet_to_cell_linksparameter onmodel_to_mesh, so there it only takes effect through that partitioner. Dispatches oninspect.signature.The default
ghost_modeisshared_facet, as forcreate_mesh, while dolfinx’s own default isnoneon every version.If
partitioneris given explicitly it is always used as-is. On 0.10 and 0.11,ghost_mode(and on 0.10max_facet_to_cell_links) then only apply if that partitioner was built with them.