Skip to content

propagate dtype through finat element construction - #263

Draft
hardik-corintis wants to merge 7 commits into
firedrakeproject:mainfrom
Corintis:fix/symbolic-tabulation-tolerance-precision
Draft

propagate dtype through finat element construction#263
hardik-corintis wants to merge 7 commits into
firedrakeproject:mainfrom
Corintis:fix/symbolic-tabulation-tolerance-precision

Conversation

@hardik-corintis

@hardik-corintis hardik-corintis commented Jul 21, 2026

Copy link
Copy Markdown

follow-up to #260
as_fiat_cell / convert_finiteelement / create_element in finat/element_factory.py now take a dtype and forward it to ufc_cell, so ref_el.vertices is built at the caller's actual precision.

firedrake side: firedrakeproject/firedrake#5033

@hardik-corintis
hardik-corintis requested a review from pbrubeck July 21, 2026 09:00
@hardik-corintis hardik-corintis changed the title Fix single-precision tolerance calibration in symbolic tabulation" Fix single-precision tolerance calibration in symbolic tabulation Jul 21, 2026
@hardik-corintis
hardik-corintis force-pushed the fix/symbolic-tabulation-tolerance-precision branch from 53f7b0e to 80c771b Compare July 21, 2026 09:10
@hardik-corintis
hardik-corintis marked this pull request as draft July 21, 2026 09:34
@pbrubeck

Copy link
Copy Markdown

How are you constructing the reference element in firedrake?

@pbrubeck

pbrubeck commented Aug 5, 2026

Copy link
Copy Markdown

self.ref_el.vertices is always float64

How can this be? Aren't you setting the precision when you construct the cell?

@hardik-corintis

hardik-corintis commented Aug 6, 2026

Copy link
Copy Markdown
Author

self.ref_el.vertices is always float64

How can this be? Aren't you setting the precision when you construct the cell?

I am a bit confused, we are constructing the cell using finat.element_factory.as_fiat_cell(cell), which calls ufc_cell(cell) — no dtype argument, no? Maybe we need to pass the dtype argument there, this is why we have float64 as default? Do you recommend changing all these call sites?

and there are the same kind of calls (no dtype passed) in a few other places too:

as_fiat_cell itself takes no dtype either:

@cache
def as_fiat_cell(cell):
"""Convert a ufl cell to a FIAT cell.
:arg cell: the :class:`ufl.Cell` to convert."""
if not isinstance(cell, ufl.AbstractCell):
raise ValueError("Expecting a UFL Cell")
return ufc_cell(cell)

Do you recommend passing dtype at all these sites?

@pbrubeck

pbrubeck commented Aug 6, 2026

Copy link
Copy Markdown

Yes, just propagate the dtype at cell construction

@pbrubeck

pbrubeck commented Aug 6, 2026

Copy link
Copy Markdown

Arguably the cell should be a global constant, we should not be creating multiple instances in firedrake. Reducing the number of cell construction calls is a separate PR

@hardik-corintis

Copy link
Copy Markdown
Author

Yes, just propagate the dtype at cell construction

Ok, so if you agree, I can revert the changes and repurpose this PR to pass the dtype argument here?

@cache
def as_fiat_cell(cell):
"""Convert a ufl cell to a FIAT cell.
:arg cell: the :class:`ufl.Cell` to convert."""
if not isinstance(cell, ufl.AbstractCell):
raise ValueError("Expecting a UFL Cell")
return ufc_cell(cell)

@pbrubeck

pbrubeck commented Aug 6, 2026

Copy link
Copy Markdown

Yes, just propagate the dtype at cell construction

Ok, so if you agree, I can revert the changes and repurpose this PR to pass the dtype argument here?

@cache
def as_fiat_cell(cell):
"""Convert a ufl cell to a FIAT cell.
:arg cell: the :class:`ufl.Cell` to convert."""
if not isinstance(cell, ufl.AbstractCell):
raise ValueError("Expecting a UFL Cell")
return ufc_cell(cell)

Sure that sounds fine to me

@hardik-corintis hardik-corintis changed the title Fix single-precision tolerance calibration in symbolic tabulation propagate dtype through finat element construction Aug 6, 2026
@hardik-corintis
hardik-corintis marked this pull request as ready for review August 6, 2026 15:30
hardik-corintis added a commit to Corintis/firedrake that referenced this pull request Aug 7, 2026
Threads dtype=RealType (or the numpy-derived real dtype from scalar_type
in TSFC) through every as_fiat_cell/create_element call site in
firedrake and tsfc, using FIAT PR firedrakeproject#260 and FInAT's new dtype support
(firedrakeproject/fiat#263). Removes the finat.element_factory.ufc_cell
monkeypatch in firedrake/utils.py, which is no longer needed now that
callers declare their working precision explicitly instead of relying
on a globally-overridden default.

Also fixes petsc_sparse's hardcoded rtol=1E-10 drop tolerance
(firedrake/preconditioners/fdm.py), which is too tight for single
precision: reference-cell geometry feeding into these matrices is only
accurate to float32 round-off (~1e-7), so entries that should be
exactly zero (e.g. in a Nedelec discrete gradient matrix) were leaking
into the sparsity pattern above the fp64-tuned threshold, corrupting
the exact combinatorial structure PETSc's PCBDDCNedelecSupport expects
and causing test_bddc_aij_simplex[N1curl-3-False] to fail.
@hardik-corintis
hardik-corintis marked this pull request as draft August 7, 2026 11:38
@hardik-corintis
hardik-corintis force-pushed the fix/symbolic-tabulation-tolerance-precision branch from c70b2ef to d9559f1 Compare August 7, 2026 15:22
…tions

Rank- and nullspace-determining linear algebra during macro-element
construction (compute_normal, spanning_basis, AlfeldSorokinaSpace,
hdiv_conforming_coefficients) needs float64 regardless of the working
precision, since float32 round-off in vertex coordinates can flip the
computed rank.
@hardik-corintis
hardik-corintis force-pushed the fix/symbolic-tabulation-tolerance-precision branch from d9559f1 to 6ec3724 Compare August 7, 2026 15:34
Comment thread FIAT/alfeld_sorokina.py Outdated

if len(rows) > 0:
dual_mat = numpy.vstack(rows)
interior_facets = ref_complex.get_interior_facets(sd-1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patching is unacceptable. This means that storing the vertices in single precision was the wrong decision

Comment thread FIAT/macro.py
rows_fp64.append(numpy.tensordot(weights_fp64, njump_fp64, axes=(ax, ax)))

dual_mat = numpy.vstack(rows_fp64)
nsp = polynomial_set.spanning_basis(dual_mat, nullspace=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just set the tolerance based on the working precision here?

Comment thread FIAT/macro.py Outdated
Comment on lines +436 to +458
def _reconstruct_split_complex_fp64(
ref_el: SimplicialComplex) -> SimplicialComplex:
"""Reconstruct a split complex from float64 root geometry.

Parameters
----------
ref_el : SimplicialComplex
The reference complex to reconstruct.

Returns
-------
SimplicialComplex
The reconstructed reference complex.
"""
if not isinstance(ref_el, SplitSimplicialComplex):
ref_el_fp64 = copy.copy(ref_el)
ref_el_fp64.vertices = reference_element.cast_vertices(
ref_el.vertices, numpy.float64)
ref_el_fp64._split_cache = {}
return ref_el_fp64

parent_fp64 = _reconstruct_split_complex_fp64(ref_el._split_parent)
return ref_el.reconstruct(parent_fp64)

@pbrubeck pbrubeck Sep 3, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to find a better solution that does not involve going back and forth between precisions.

More fundamentally, we need to decide whether Firedrake float32 even needs a FIAT reimplementation. For most elements we can get away by casting the numerical results at the very end. The main issue was that symbolic FIAT tabulations need to be casted differently.

Tell Claude to find the path of least resistance (whichever is easier to maintain): to let FIAT compute everything for a single dtype or to compute everything in double and cast the final result to the target dtype.

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.

2 participants