Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
fac7012
Fix single-precision (fp32) support: TSFC initializer dtype cast and …
hardik-corintis Apr 9, 2026
fcf7b57
Replacing double with Petsc.RealType
hardik-corintis Apr 9, 2026
561feed
Use PETSc types for precision and integer width in C code
hardik-corintis Apr 9, 2026
47a1ad2
Replace hardcoded dtypes with PETSc-derived types
hardik-corintis Apr 9, 2026
e38b9a7
Add single-precision (fp32) build support and detection
hardik-corintis Apr 9, 2026
39f82af
Fix sparsity.pyx: revert broken PetscScalar cimport
hardik-corintis Apr 9, 2026
fa4b924
skipping tests for single precision
hardik-corintis Apr 9, 2026
d3d8aec
DBL_MAX to PETSC_MAX_REAL
hardik-corintis Apr 15, 2026
2a0b767
fp32 detection in tests
hardik-corintis Apr 15, 2026
b0bbca6
ensuring that coordinates are real
hardik-corintis Apr 15, 2026
00b7f75
Cleaning up
hardik-corintis Apr 15, 2026
a986f20
Fix PetscScalar/PetscReal type mismatch in prolong/restrict kernels a…
hardik-corintis Apr 15, 2026
6ddb6ae
restoring tests/test_durations.json
hardik-corintis Apr 15, 2026
3076a4d
reverting solver parameters back to original form
hardik-corintis Apr 15, 2026
d65cd77
Port missing dtype=int -> IntType fixes
hardik-corintis Apr 15, 2026
64cce3f
adding dtypes for int
hardik-corintis Apr 28, 2026
21337e9
Update skipsingle comment for test_parallel_high_order_location
hardik-corintis May 4, 2026
15fd4cc
removed alias fp32
hardik-corintis May 10, 2026
b8a679f
Add single-complex arch to firedrake-configure; drop _fp32 alias in t…
hardik-corintis May 10, 2026
3cd08b8
Fix duplicate SINGLE arch keys and missing CC in UNKNOWN SINGLE_COMPLEX
hardik-corintis May 21, 2026
088e87e
Add comments explaining Cython typedef nominal hint for fp32 builds
hardik-corintis May 21, 2026
af8f153
convergence eps constant, PETSC_MAX_REAL, tsfc type guard, skipsingle…
hardik-corintis May 21, 2026
57be852
IntType cleanup, arch moves to CommunityArch, test tolerance fixes, f…
hardik-corintis May 21, 2026
326b0d9
Restore blank lines
hardik-corintis May 21, 2026
e13b4e1
cleaning up comments
hardik-corintis May 21, 2026
40ab2d8
Update .github/workflows/push.yml
hardik-corintis May 21, 2026
2af6283
fixing linting
hardik-corintis May 21, 2026
e868f52
addressing comments from Connor
hardik-corintis May 27, 2026
2f3834d
Merge branch 'firedrakeproject:main' into fp32-support
hardik-corintis May 27, 2026
78bd945
ci:single-complex label and test_single_complex CI input
hardik-corintis May 28, 2026
d789bcd
adding a missing hypen in "--download-mumps-avoid-mpi-in-place"
hardik-corintis May 28, 2026
b33ee9c
adding flag so hypre compiles with single precision
hardik-corintis Jun 2, 2026
911bcd4
fixing a unit test
hardik-corintis Jun 2, 2026
083feda
removing superlu from complex-single
hardik-corintis Jun 2, 2026
557df42
removing hypre from single-complex
hardik-corintis Jun 2, 2026
33e2f35
fix pyop2 tests
hardik-corintis Jun 2, 2026
be2fdf5
adjoint: fix 0-form taping for fp32 scalar returns numpy.float32
hardik-corintis Jun 2, 2026
d999cfb
tests/equation_bcs: adapt tolerances to fp32 machine epsilon
hardik-corintis Jun 2, 2026
a8e8a90
tests/ensemble: adapt tolerances to fp32 machine epsilon
hardik-corintis Jun 2, 2026
25215ca
cast PETSc matrix values to ScalarType
hardik-corintis Jun 3, 2026
f510500
mg, function: fix point location and grid transfer for fp32
hardik-corintis Jun 5, 2026
09c263b
tests: adapt tolerances and document skips for fp32 single precision
hardik-corintis Jun 5, 2026
5697dc8
tests/adjoint: use single_mode and adapt tolerances
hardik-corintis Jun 5, 2026
279f1f0
use int64 buffers for libsupermesh intersection_finder
hardik-corintis Jun 5, 2026
e0bb4a3
restore the tol for fp64
hardik-corintis Jun 5, 2026
0f60f0b
updating few more tests for fp32 case
hardik-corintis Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ on:
description: Whether to test a complex build
type: boolean
required: true
test_single:
description: Whether to test a single-precision (fp32) build
type: boolean
required: true
test_single_complex:
description: Whether to test a single-precision complex build
type: boolean
required: true
test_cuda:
description: Whether to test using CUDA-enabled PETSc
type: boolean
Expand Down Expand Up @@ -54,6 +62,14 @@ on:
description: Whether to test a complex build
type: boolean
required: true
test_single:
description: Whether to test a single-precision (fp32) build
type: boolean
required: true
test_single_complex:
description: Whether to test a single-precision complex build
type: boolean
required: true
test_cuda:
description: Whether to test using CUDA-enabled PETSc
type: boolean
Expand Down Expand Up @@ -264,6 +280,22 @@ jobs:
if [ ${{ inputs.test_complex }} == 'true' ]; then
matrix='{"scalar_type": "complex", "gpu": "none"}'
fi
if [ ${{ inputs.test_single }} == 'true' ]; then
arch='{"scalar_type": "single", "gpu": "none"}'
if [ "$matrix" ]; then
matrix="$matrix, $arch"
else
matrix="$arch"
fi
fi
if [ ${{ inputs.test_single_complex }} == 'true' ]; then
arch='{"scalar_type": "single-complex", "gpu": "none"}'
if [ "$matrix" ]; then
matrix="$matrix, $arch"
else
matrix="$arch"
fi
fi
if [ ${{ inputs.test_cuda }} == 'true' ]; then
arch='{"scalar_type": "default", "gpu": "cuda"}'
if [ "$matrix" ]; then
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ jobs:
# Run all tests of a specific configuration (e.g. complex) if the right label
# is on the PR, otherwise do nothing.
test_complex: ${{ contains(github.event.pull_request.labels.*.name, 'ci:complex') }}
test_single: ${{ contains(github.event.pull_request.labels.*.name, 'ci:single') }}
test_single_complex: ${{ contains(github.event.pull_request.labels.*.name, 'ci:single-complex') }}
test_cuda: ${{ contains(github.event.pull_request.labels.*.name, 'ci:cuda') }}
test_macos: ${{ contains(github.event.pull_request.labels.*.name, 'ci:macos') }}
secrets: inherit
2 changes: 2 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
source_ref: ${{ github.ref_name }}
base_ref: ${{ github.ref_name }}
test_complex: true
test_single: true
test_single_complex: true
test_cuda: true
test_macos: true
deploy_website: true
Expand Down
6 changes: 5 additions & 1 deletion firedrake/adjoint_utils/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ def wrapper(form, *args, **kwargs):
if not annotate:
return output

if not isinstance(output, (float, Function, Cofunction)):
if not isinstance(output, (numbers.Real, Function, Cofunction)):
raise NotImplementedError("Taping for complex-valued 0-forms not yet done!")
# pyadjoint's AdjFloat requires Python float; coerce numpy scalars
# (numpy.float64 is a float subclass so this is a no-op in fp64 mode)
if not isinstance(output, (float, Function, Cofunction)):
output = float(output)
output = create_overloaded_object(output)
block = AssembleBlock(form, ad_block_tag=ad_block_tag)

Expand Down
7 changes: 5 additions & 2 deletions firedrake/adjoint_utils/blocks/assembly.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import numbers
import ufl
import firedrake
from ufl.domain import extract_domains
Expand Down Expand Up @@ -136,7 +137,7 @@ def evaluate_tlm_component(self, inputs, tlm_inputs, block_variable, idx,
else:
dform += firedrake.action(firedrake.derivative(form, c_rep),
tlm_value)
if not isinstance(dform, float):
if not isinstance(dform, numbers.Real):
dform = ufl.algorithms.expand_derivatives(dform)
dform = firedrake.assemble(dform)
return dform
Expand Down Expand Up @@ -184,7 +185,7 @@ def evaluate_hessian_component(self, inputs, hessian_inputs, adj_inputs,
else:
ddform += firedrake.derivative(dform, c2_rep, tlm_input)

if not isinstance(ddform, float):
if not isinstance(ddform, numbers.Real):
ddform = ufl.algorithms.expand_derivatives(ddform)
if not (isinstance(ddform, ufl.ZeroBaseForm)
or (isinstance(ddform, ufl.Form) and ddform.empty())):
Expand All @@ -200,6 +201,8 @@ def prepare_recompute_component(self, inputs, relevant_outputs):
def recompute_component(self, inputs, block_variable, idx, prepared):
form = prepared
output = firedrake.assemble(form)
if isinstance(output, numbers.Real) and not isinstance(output, float):
output = float(output)
output = create_overloaded_object(output)
if isinstance(output, firedrake.Function):
return maybe_disk_checkpoint(output)
Expand Down
1 change: 1 addition & 0 deletions firedrake/cython/petschdr.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ cdef extern from "mpi-compat.h":

cdef extern from "petsc.h":
ctypedef long PetscInt
# Nominal hints only: C compiler resolves PetscReal/PetscScalar from petsc.h, so fp32 builds are correct.
ctypedef double PetscReal
ctypedef double PetscScalar
ctypedef enum PetscBool:
Expand Down
19 changes: 10 additions & 9 deletions firedrake/evaluate.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ extern "C" {

struct Function {
/* Number of cells in the base mesh */
int n_cols;
PetscInt n_cols;

/* 1 if extruded, 0 if not */
int extruded;

/* number of layers for extruded, otherwise 1 */
int n_layers;
PetscInt n_layers;

/* Coordinate values and node mapping */
PetscScalar *coords;
Expand All @@ -36,26 +36,27 @@ struct Function {

typedef PetscReal (*ref_cell_l1_dist)(void *data_,
struct Function *f,
int cell,
PetscInt cell,
double *x);

typedef PetscReal (*ref_cell_l1_dist_xtr)(void *data_,
struct Function *f,
int cell,
int layer,
PetscInt cell,
PetscInt layer,
double *x);

extern int locate_cell(struct Function *f,
extern PetscInt locate_cell(struct Function *f,
double *x,
int dim,
ref_cell_l1_dist try_candidate,
ref_cell_l1_dist_xtr try_candidate_xtr,
void *temp_ref_coords,
void *found_ref_coords,
double *found_ref_cell_dist_l1,
size_t ncells_ignore,
int* cells_ignore);
PetscReal *found_ref_cell_dist_l1,
size_t ncells_ignore,
PetscInt* cells_ignore);

/* x is physical coordinates: always double (libspatialindex requires float64). */
extern int evaluate(struct Function *f,
double *x,
PetscScalar *result);
Expand Down
8 changes: 5 additions & 3 deletions firedrake/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@

class _CFunction(ctypes.Structure):
r"""C struct collecting data from a :class:`Function`"""
_fields_ = [("n_cols", c_int),
_fields_ = [("n_cols", as_ctypes(IntType)),
("extruded", c_int),
("n_layers", c_int),
("n_layers", as_ctypes(IntType)),
("coords", c_void_p),
("coords_map", POINTER(as_ctypes(IntType))),
("f", c_void_p),
Expand Down Expand Up @@ -564,7 +564,7 @@ def evaluate(self, coord, mapping, component, index_values):
# Called by UFL when evaluating expressions at coordinates
if component or index_values:
raise NotImplementedError("Unsupported arguments when attempting to evaluate Function.")
coord = np.asarray(coord, dtype=utils.ScalarType)
coord = np.asarray(coord)
evaluator = PointEvaluator(self.function_space().mesh(), coord)
result = evaluator.evaluate(self)
if len(coord.shape) == 1:
Expand Down Expand Up @@ -607,6 +607,8 @@ def _at(self, arg, *args, **kwargs):
if not np.allclose(arg.imag, 0):
raise ValueError("Provided points have non-zero imaginary part")
arg = arg.real.copy()
# Point location (libspatialindex) needs float64 coords, not ScalarType.
arg = np.asarray(arg, dtype=np.float64)

dont_raise = kwargs.get('dont_raise', False)

Expand Down
4 changes: 3 additions & 1 deletion firedrake/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,9 @@ def _create_permutation_mat(self, mat_type: Literal["aij", "baij"]) -> PETSc.Mat
# Vector and Tensor valued functions are stored in a flattened array, so
# we need to space out the column indices according to the block size
cols = (self.target_space.block_size * perm[:, None] + numpy.arange(self.target_space.block_size, dtype=IntType)[None, :]).reshape(-1)
mat.setValuesCSR(rows, cols, numpy.ones_like(cols, dtype=IntType))
# Matrix values must be PetscScalar, not IntType: petsc4py rejects an unsafe
# int -> float32 cast when PETSc is built in single precision.
mat.setValuesCSR(rows, cols, numpy.ones_like(cols, dtype=ScalarType))
mat.assemble()
if self.forward_reduce and not self.ufl_interpolate.is_adjoint:
# The mat we have constructed thus far takes us from the input-ordering VOM to the
Expand Down
19 changes: 9 additions & 10 deletions firedrake/locate.c
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <rtree-capi.h>
#include <float.h>
#include <evaluate.h>

int locate_cell(struct Function *f,
PetscInt locate_cell(struct Function *f,
double *x,
int dim,
ref_cell_l1_dist try_candidate,
ref_cell_l1_dist_xtr try_candidate_xtr,
void *temp_ref_coords,
void *found_ref_coords,
double *found_ref_cell_dist_l1,
PetscReal *found_ref_cell_dist_l1,
size_t ncells_ignore,
int* cells_ignore)
PetscInt* cells_ignore)
{
RTreeError err;
int cell = -1;
PetscInt cell = -1;
int cell_ignore_found = 0;
/* NOTE: temp_ref_coords and found_ref_coords are actually of type
struct ReferenceCoords but can't be declared as such in the function
Expand All @@ -25,8 +24,8 @@ int locate_cell(struct Function *f,
surrounds this is declared in pointquery_utils.py. We cast when we use the
ref_coords_copy function and trust that the underlying memory which the
pointers refer to is updated as necessary. */
double ref_cell_dist_l1 = DBL_MAX;
double current_ref_cell_dist_l1 = -0.5;
PetscReal ref_cell_dist_l1 = PETSC_MAX_REAL;
PetscReal current_ref_cell_dist_l1 = -0.5;
/* NOTE: `tolerance`, which is used throughout this funciton, is a static
variable defined outside this function when putting together all the C
code that needs to be compiled - see pointquery_utils.py */
Expand Down Expand Up @@ -73,9 +72,9 @@ int locate_cell(struct Function *f,
}
else {
for (size_t i = 0; i < nids; i++) {
int nlayers = f->n_layers;
int c = ids[i] / nlayers;
int l = ids[i] % nlayers;
PetscInt nlayers = f->n_layers;
PetscInt c = ids[i] / nlayers;
PetscInt l = ids[i] % nlayers;
current_ref_cell_dist_l1 = (*try_candidate_xtr)(temp_ref_coords, f, c, l, x);
for (size_t j = 0; j < ncells_ignore; j++) {
if (ids[i] == cells_ignore[j]) {
Expand Down
Loading
Loading