Basis function ordering using T_apply

Hello guys!

I was exploring quadrature rules and manual integration in dolfinx while encountered something that seems like a problem. When manually assembling any matrix, it is necessary to know which basis value computed in some point (quadrature for example, but not necessary) corresponds to which cell dof. From documentation it looks like function T_apply (dolfinx.fem — DOLFINx 0.10.0.0 documentation) should do what I’m looking for and rearrange basis functions values to match dof ordering. However, in my MWE I do not notice any difference after calling T_apply:

import basix

import dolfinx

import numpy as np

from mpi4py import MPI

# Define mesh

mesh = dolfinx.mesh.create_unit_square(MPI.COMM_SELF, 2, 2, cell_type=dolfinx.mesh.CellType.triangle)

V = dolfinx.fem.functionspace(mesh, ("Lagrange", 1))

# Define quadrature rule

order=2

quad_points, weights = basix.make_quadrature(cell=V.mesh.basix_cell(),degree=order)

# Tabuate basis function values

basis_values = V.element.basix_element.tabulate(0, quad_points)[0, :, :, 0]

# Compute permutation data

mesh.topology.create_entity_permutations()

cell_permutations = mesh.topology.get_cell_permutation_info()

print(cell_permutations)

# Apply permutations to get basis function values in real cells

basis_values_extended = np.tile(basis_values.flatten(),len(cell_permutations))

print("before \n")

print(basis_values_extended.reshape((len(cell_permutations),*basis_values.shape)))

V.element.T_apply(basis_values_extended,cell_permutations,mesh.topology.dim)

print("after \n")

print(basis_values_extended.reshape((len(cell_permutations),*basis_values.shape)))

I would really appreciate any explanation of T_apply behavior and errors that I’ve possibly made. Note that using mesh.topology.dim**2 as last argument for T_apply introduces no changes at all which seems odd. Same result is true for unit cube meshed with tetrahedrons.

Regarding specs, here are versions of related packages that I use:

T_apply is a dof transformation applied to elements which require them, see section 8.4 of: DOLFINx: The next generation FEniCS problem solving environment

Lagrange elements are not such an element, as the dofmap is permitted at the creation of the function space to account for a consistent ordering.

What you seem like you would like is the element-dof-layout: dolfinx.cpp.fem — DOLFINx 0.10.0.0 documentation
which tells you how each dof in V.cell_dofs(i) relates to the element entities. You can either look at the closure (for instance all dofs related to a facet, the edges of a facet and the vertices), or just those associated with such an entity.

See for instance: Moving submesh diverges from its designated trajectory in a two-phase problem - #2 by dokken for a use-case of finding the dofs associated with a given set of vertices.

Thanks for the swift response!

Do I understand correctly, that if (for 2D case) calling V.dofmap.dof_layout.entity_closure_dofs(2,0) yieds [0,1,2], then no matter how each cell is oriented, dof indices returned by V.dofmap.cell_dofs(i) are arranged in consistency with reference element? For example, if cell dofs are [0,3,2], than tabulating basis function values in any point inside this cell will return triplet where first value corresponds to dof 0, second to dof 3 and last one for dof 2?

Instead of calling closure dofs, I would call entity_dofs(2,0), then (2,1), then (2,2) to get which dof is associated with the 1st,2nd and 3rd vertex (as you avoid assumptions on the order in entity closure dofs).

With respect to the order, if the above returns (0),(1),(2) then the vertices of the reference element aligns with the tabulated basis functions.

If cell dofs(i) align as this, then you have that the first vertex of cell i has number 0 (global number, local to process), the second has 3, etc.

Note that if you are trying to align this with the geometrical nodes of the mesh, you should use entities_to_geometry to map each vertex to the node in the geometry. See: Locate_entities returning incorrect points with MPI - #4 by dokken
for an example.

I suppose, you meant entity_dofs(0,0), entity_dofs(0,1) and entity_dofs(0,2)? Yes, they return [0],[1] and [2]. However, I was a bit confused by this function as none of the higher dimension entities are indexed. Calling entity_dofs(1,0) as well as entity_dofs(2,0) will result in []. Same I see for tetrahedrons, the function can accept expected number of entities (up to 4 faces for example), but will always return empty list for any dim except 0. What is the idea behind this function then?

Yes, sorry. I’m on my phone atm, so typing sometime is a bit too fast.

I guess you are «only» considering first order Lagrange functions. DOLFINx supports many other elements of various degrees, where dogs are associated with entities. You can for instance see this for a third order element at:

I checked function’s behavior with different types of elements and different orders and now it makes sense. Thanks a lot!