Skip to content

Fix right DOF transformation for mixed elements with block size > 1 - #4464

Open
PaulXiCao wants to merge 4 commits into
FEniCS:mainfrom
PaulXiCao:fix/4294-dof-transformation-right
Open

Fix right DOF transformation for mixed elements with block size > 1#4464
PaulXiCao wants to merge 4 commits into
FEniCS:mainfrom
PaulXiCao:fix/4294-dof-transformation-right

Conversation

@PaulXiCao

@PaulXiCao PaulXiCao commented Aug 31, 2026

Copy link
Copy Markdown

Fixes #4294.

In the mixed-element branch of FiniteElement::dof_transformation_right_fn,
the data passed to each sub-element was sliced as

data.subspan(offset, data.size() - offset)

which treats the buffer as a single contiguous run. For a right-applied transformation the layout is (block_size rows) x (ndofs columns), so sub-element e owns columns [offset, offset + dims[e]) of every row. Basix derives the row stride as data.size() / n, so trimming the front of the span shortened the stride by offset / n and misaligned every row after the first. Only block_size == 1, where the trimmed prefix is absorbed exactly, was correct.

Each row is now transformed separately over the sub-element's own column range. This affects bilinear-form matrix assembly for mixed elements needing DOF transformations, where the transposed transformation is applied with one row per test-function DOF.

Tests

cpp/test/fem/finite_element.cpp compares a multi-row application against a row-by-row application through the block_size == 1 path, for a mixed element with the transforming sub-element at a non-zero DOF offset (mixed(P1 Lagrange, N1curl degree 2) on a triangle). Two control cases cover the same element at offset 0 and the non-mixed leaf path, both of which were already correct. All cases are parametrised over the four doftransform values, each of which failed independently before the fix.

Note on performance

The sub-element transformation is now invoked once per row rather than once per sub-element, which loses the batching Basix does internally over n. Basix's right-apply already supports a column offset internally (apply_tranpose_matrix_right indexes data[data_size * b + offset + i]), so exposing that in the public *_apply_right signature would let this be a single call per sub-element again. Happy to follow up upstream if wanted.


AI assistance: I used Claude Code (Opus 5) to draft parts of this PR. I reviewed, edited, tested, and take responsibility for the final contribution.

PaulXiCao and others added 3 commits August 31, 2026 23:04
In the mixed-element branch of dof_transformation_right_fn, the data
passed to each sub-element was sliced as

  data.subspan(offset, data.size() - offset)

which treats the buffer as a single contiguous run. For a right-applied
transformation the layout is (block_size rows) x (ndofs columns), so
sub-element e owns columns [offset, offset + dims[e]) of *every* row.
Basix derives the row stride as data.size() / n, so trimming the front
of the span shortened the stride by offset / n and misaligned every row
after the first. Only block_size == 1, where the trimmed prefix is
absorbed exactly, was correct.

Transform each row separately over the sub-element's own column range.
This affects bilinear-form matrix assembly for mixed elements needing
DOF transformations, where the transposed transformation is applied
with one row per test-function DOF.

Note that the sub-element transformation is now invoked once per row
rather than once per sub-element, which loses the batching Basix does
internally over n. Passing a column offset down to Basix would restore
it without the per-row calls.

Closes FEniCS#4294

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sub-span slicing in the mixed-element branch of
dof_transformation_right_fn is shared by all four doftransform values,
so cover them all rather than only the transpose that matrix assembly
happens to use. Each case failed independently before the slicing fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jhale

jhale commented Sep 1, 2026

Copy link
Copy Markdown
Member

Could you write the tests in the Python at python/test/unit/fem? Perhaps there is an appropriate file with tests already related to transforms. We prefer Python for test writing unless there is a good specific reason to use C++.

Comment thread cpp/test/fem/finite_element.cpp Outdated
@PaulXiCao

Copy link
Copy Markdown
Author

Could you write the tests in the Python at python/test/unit/fem? Perhaps there is an appropriate file with tests already related to transforms. We prefer Python for test writing unless there is a good specific reason to use C++.

Hi @jhale . I am a new contributor and might need some guidance, but AFAI can tell there is no wrapper for either dof_transformation_fn function. I checked python/dolfinx/wrappers/dolfinx_wrappers/fem.h:ll.132-345 and also just found these occurrences:

$ rg "dof_transformation" python 
python/dolfinx/fem/element.py
301:    def needs_dof_transformations(self) -> bool:
314:        return self._cpp_object.needs_dof_transformations

python/dolfinx/wrappers/dolfinx_wrappers/fem.h
341:        .def_prop_ro("needs_dof_transformations",
342:                     &dolfinx::fem::FiniteElement<T>::needs_dof_transformations)

Do you know if these were deliberately left out of the python wrapper? Otherwise i could look into adding them to the wrapper in a follow-up issue.

Or was your idea to write a python test where we directly call down into the c++ layer (similar to writing a temporary wrapper just for the test)?

@jhale

jhale commented Sep 4, 2026

Copy link
Copy Markdown
Member

They were probably left out because they are performance critical functions that should not be called from Python.

What I would propose is that they are wrapped into dolfinx.cpp as _function_name, indicating that they are private, but not wrapped into the Python layer, which defines our public API. We can then unit test them from Python.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: dof_transformation_right_fn mis-slices data for mixed elements with block_size > 1

3 participants