#1152872 some mesh and petsc tests fail with complex numbers

#1152872#5
Date:
2026-11-02 18:07:13 UTC
From:
To:
test_form_map_returns_index_map_and_block_size in test_mesh.py and
test_set_bc_applies_dirichlet_value,
test_apply_lifting_and_set_bc_runs_on_a_simple_poisson_system_* in test_petsc.py
are failing with complex numbers.

e.g. test_mesh.py, test_form_map_returns_index_map_and_block_size

$ PETSC_DIR=/usr/lib/petscdir/petsc-complex pytest-3 -v test_mesh.py -k test_form_map_returns_index_map_and_block_size
============================================================================ test session starts ============================================================================
platform linux -- Python 3.14.10, pytest-9.1.1, pluggy-1.6.0 -- /usr/bin/python3
cachedir: .pytest_cache
hypothesis profile 'default'
Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
rootdir: /home/projects/fenics/build/fenicsx-compat
configfile: pyproject.toml
plugins: anyio-4.15.1, timeout-2.4.0, hypothesis-6.155.7, mock-3.15.1, xdist-3.8.0, random-order-1.2.0+ds, typeguard-4.6.0, flaky-3.8.1, cases-3.10.1, mpi-0.6, cov-7.1.0, zarr-3.4.2, asyncio-1.4.0
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 15 items / 14 deselected / 1 selected

test_mesh.py::test_form_map_returns_index_map_and_block_size FAILED                                                                                                   [100%]

================================================================================= FAILURES ==================================================================================
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

test_mesh.py:58:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/forms.py:479: in form
    return _create_form(form)
           ^^^^^^^^^^^^^^^^^^
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/forms.py:471: in _create_form
    return _form(form)
           ^^^^^^^^^^^
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/forms.py:387: in _form
    ufcx_form, module, code = jit.ffcx_jit(
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/jit.py:61: in mpi_jit
    return local_jit(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/jit.py:221: in ffcx_jit
    r = ffcx.codegeneration.jit.compile_forms([ufl_object], options=p_ffcx, **p_jit)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:224: in compile_forms
    impl = _compile_objects(
/usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:349: in _compile_objects
    code, _ = ffcx.compiler.compile_ufl_objects(
/usr/lib/python3/dist-packages/ffcx/compiler.py:111: in compile_ufl_objects
    analysis = analyze_ufl_objects(ufl_objects, options["scalar_type"])  # type: ignore
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/python3/dist-packages/ffcx/analysis.py:91: in analyze_ufl_objects
    form_data = tuple(_analyze_form(form, scalar_type) for form in forms)
                ^^^^^
/usr/lib/python3/dist-packages/ffcx/analysis.py:91: in <genexpr>
    form_data = tuple(_analyze_form(form, scalar_type) for form in forms)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/python3/dist-packages/ffcx/analysis.py:175: in _analyze_form
    form_data: FormData = ufl.algorithms.compute_form_data(
/usr/lib/python3/dist-packages/ufl/algorithms/compute_form_data.py:197: in compute_form_data
    return FormData(
/usr/lib/python3/dist-packages/ufl/algorithms/formdata.py:341: in __init__
    _check_form_arity(self.integral_data, self.original_form.arguments(), complex_mode)
/usr/lib/python3/dist-packages/ufl/algorithms/formdata.py:36: in _check_form_arity
    check_integrand_arity(itg.integrand(), arguments, complex_mode)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

expr = Product(Product(QuadratureWeight(Mesh(blocked element (Basix element (P, triangle, 1, gll_warped, unset, False, float6...set, False, float64, []), (2,)), 0), Basix element (P, triangle, 1, gll_warped, unset, False, float64, [])), 0, None)))
arguments = (Argument(FunctionSpace(Mesh(blocked element (Basix element (P, triangle, 1, gll_warped, unset, False, float64, []), (2,)), 0), Basix element (P, triangle, 1, gll_warped, unset, False, float64, [])), 0, None),)
complex_mode = True

    def check_integrand_arity(expr, arguments, complex_mode=False):
        """Check the arity of an integrand."""
        arguments = tuple(sorted(set(arguments), key=lambda x: (x.number(), x.part())))
        rules = ArityChecker(arguments)
        arg_tuples = map_expr_dag(rules, expr, compress=False)
        args = tuple(a[0] for a in arg_tuples)
        if args != arguments:
            raise ArityMismatch(f"Integrand arguments {args} differ from form arguments {arguments}.")
        if complex_mode:
            # Check that the test function is conjugated and that any
            # trial function is not conjugated. Further arguments are
            # treated as trial funtions (i.e. no conjugation) but this
            # might not be correct.
            for arg, conj in arg_tuples:
                if arg.number() == 0 and not conj:
/usr/lib/python3/dist-packages/ufl/algorithms/check_arities.py:205: ArityMismatch
========================================================================== short test summary info ==========================================================================
FAILED test_mesh.py::test_form_map_returns_index_map_and_block_size - ufl.algorithms.check_arities.ArityMismatch: Failure to conjugate test function in complex Form
===================================================================== 1 failed, 14 deselected in 0.33s ======================================================================

The petsc error seems a little different:

/usr/lib/python3/dist-packages/fenicsx_compat/petsc.py:39:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/petsc.py:667: in set_bc
    bc.set(b.array_w, x0, alpha)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <dolfinx.fem.bcs.DirichletBC object at 0x7fe0a047d090>, x = array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,
       0.+0.j]), x0 = None
alpha = 1.0

    def set(
        self, x: npt.NDArray[Scalar], x0: npt.NDArray[Scalar] | None = None, alpha: float = 1
    ) -> None:
        """Set array entries that are constrained by a Dirichlet condition.

        Entries in ``x`` that are constrained by a Dirichlet boundary
        conditions are set to ``alpha * (x_bc - x0)``, where ``x_bc`` is
        the (interpolated) boundary condition value.

        For elements with point-wise evaluated degrees-of-freedom, e.g.
        Lagrange elements, ``x_bc`` is the value of the boundary condition
        at the degree-of-freedom. For elements with moment
        degrees-of-freedom, ``x_bc`` is the value of the boundary condition
        interpolated into the finite element space.

        If `x` includes ghosted entries (entries available on the calling
        rank but owned by another rank), ghosted entries constrained by a
        Dirichlet condition will also be set.

        Args:
            x: Array to modify for Dirichlet boundary conditions.
            x0: Optional array used in computing the value to set. If
                not provided it is treated as zero.
            alpha: Scaling factor.
        """
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/bcs.py:159: TypeError

During handling of the above exception, another exception occurred:

comm = <mpi4py.MPI.Intracomm object at 0x7fe0a4981cb0>

    def test_set_bc_applies_dirichlet_value(comm):
        msh = dolfinx.mesh.create_unit_square(comm, 2, 2)
        V = dolfinx.fem.functionspace(msh, ("Lagrange", 1))
        tdim = msh.topology.dim
        msh.topology.create_connectivity(tdim - 1, tdim)
        facets = dolfinx.mesh.exterior_facet_indices(msh.topology)
        dofs = dolfinx.fem.locate_dofs_topological(V, tdim - 1, facets)
        bc = dolfinx.fem.dirichletbc(5.0, dofs, V)

        b = dolfinx.fem.petsc.create_vector(V)
        b.array[:] = 0.0

test_petsc.py:109:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3/dist-packages/fenicsx_compat/petsc.py:46: in set_bc
    dolfinx.fem.petsc.set_bc(b, bcs[0], x0=x0, alpha=alpha)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

b = <petsc4py.PETSc.Vec object at 0x7fe0a526f290>, bcs = <dolfinx.fem.bcs.DirichletBC object at 0x7fe0a047d090>, x0 = None, alpha = 1.0

    def set_bc(
        b: PETSc.Vec,  # type: ignore[name-defined]
        bcs: Sequence[DirichletBC] | Sequence[Sequence[DirichletBC]],
        x0: PETSc.Vec | None = None,  # type: ignore[name-defined]
        alpha: float = 1,
    ) -> None:
        """Set constraint (Dirchlet boundary condition) values in an vector.

        For degrees-of-freedoms that are constrained by a Dirichlet boundary
        condition, this function sets that degrees-of-freedom to ``alpha *
        (g - x0)``, where ``g`` is the boundary condition value.

        Only owned entries in ``b`` (owned by the MPI process) are modified
        by this function.

        Args:
            b: Vector to modify by setting  boundary condition values.
            bcs: Boundary conditions to apply. If ``b`` is nested or
                blocked, ``bcs`` is a 2D array and ``bcs[i]`` are the
                boundary conditions to apply to block/nest ``i``. Otherwise
                ``bcs`` should be a sequence of ``DirichletBC``. For
                block/nest problems, :func:`dolfinx.fem.bcs_by_block` can be
                used to prepare the 2D array of ``DirichletBC`` objects.
            x0: Vector used in the value that constrained entries are set
                to. If ``None``, ``x0`` is treated as zero.
            alpha: Scalar value used in the value that constrained entries
                are set to.
        """
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/petsc.py:661: TypeError

and
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

test_petsc.py:122:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/forms.py:479: in form
    return _create_form(form)
           ^^^^^^^^^^^^^^^^^^
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/forms.py:471: in _create_form
    return _form(form)
           ^^^^^^^^^^^
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/fem/forms.py:387: in _form
    ufcx_form, module, code = jit.ffcx_jit(
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/jit.py:61: in mpi_jit
    return local_jit(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/petscdir/petsc-complex/lib/python3/dist-packages/dolfinx/jit.py:221: in ffcx_jit
    r = ffcx.codegeneration.jit.compile_forms([ufl_object], options=p_ffcx, **p_jit)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:224: in compile_forms
    impl = _compile_objects(
/usr/lib/python3/dist-packages/ffcx/codegeneration/jit.py:349: in _compile_objects
    code, _ = ffcx.compiler.compile_ufl_objects(
/usr/lib/python3/dist-packages/ffcx/compiler.py:111: in compile_ufl_objects
    analysis = analyze_ufl_objects(ufl_objects, options["scalar_type"])  # type: ignore
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/python3/dist-packages/ffcx/analysis.py:91: in analyze_ufl_objects
    form_data = tuple(_analyze_form(form, scalar_type) for form in forms)
                ^^^^^
/usr/lib/python3/dist-packages/ffcx/analysis.py:91: in <genexpr>
    form_data = tuple(_analyze_form(form, scalar_type) for form in forms)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/usr/lib/python3/dist-packages/ffcx/analysis.py:175: in _analyze_form
    form_data: FormData = ufl.algorithms.compute_form_data(
/usr/lib/python3/dist-packages/ufl/algorithms/compute_form_data.py:197: in compute_form_data
    return FormData(
/usr/lib/python3/dist-packages/ufl/algorithms/formdata.py:341: in __init__
    _check_form_arity(self.integral_data, self.original_form.arguments(), complex_mode)
/usr/lib/python3/dist-packages/ufl/algorithms/formdata.py:36: in _check_form_arity
    check_integrand_arity(itg.integrand(), arguments, complex_mode)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

expr = Product(Product(QuadratureWeight(Mesh(blocked element (Basix element (P, triangle, 1, gll_warped, unset, False, float6...set, False, float64, []), (2,)), 4), Basix element (P, triangle, 1, gll_warped, unset, False, float64, [])), 0, None)))
arguments = (Argument(FunctionSpace(Mesh(blocked element (Basix element (P, triangle, 1, gll_warped, unset, False, float64, []), (2,)), 4), Basix element (P, triangle, 1, gll_warped, unset, False, float64, [])), 0, None),)
complex_mode = True

    def check_integrand_arity(expr, arguments, complex_mode=False):
        """Check the arity of an integrand."""
        arguments = tuple(sorted(set(arguments), key=lambda x: (x.number(), x.part())))
        rules = ArityChecker(arguments)
        arg_tuples = map_expr_dag(rules, expr, compress=False)
        args = tuple(a[0] for a in arg_tuples)
        if args != arguments:
            raise ArityMismatch(f"Integrand arguments {args} differ from form arguments {arguments}.")
        if complex_mode:
            # Check that the test function is conjugated and that any
            # trial function is not conjugated. Further arguments are
            # treated as trial funtions (i.e. no conjugation) but this
            # might not be correct.
            for arg, conj in arg_tuples:
                if arg.number() == 0 and not conj:
/usr/lib/python3/dist-packages/ufl/algorithms/check_arities.py:205: ArityMismatch

See also https://ci.debian.net/packages/f/fenicsx-compat/unstable/amd64/77407631/

The failed ffcx compilation then triggers "TimeoutError: JIT
compilation timed out, probably due to a failed previous compile. Try
cleaning cache (e.g. remove /home/projects/.cache/fenics/libffcx_"
when running again the next time.

Some complex number problems with xdmf were recently fixed in
io4dolfinx, though the signature of the error here looks completely
different so I think it's not the same issue.