Skip to content

Commit

Permalink
Fixes for updated PETSc (#3853)
Browse files Browse the repository at this point in the history
* Add petsc_raises fixture to catch PETSc callback exceptions
  • Loading branch information
connorjward authored Nov 12, 2024
1 parent 29c486d commit ecc6886
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 17 deletions.
33 changes: 32 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Global test configuration."""

import pytest
from firedrake.petsc import get_external_packages
from firedrake.petsc import PETSc, get_external_packages


def pytest_configure(config):
Expand Down Expand Up @@ -122,3 +122,34 @@ def fin():
assert len(tape.get_blocks()) == 0

request.addfinalizer(fin)


class _petsc_raises:
"""Context manager for catching PETSc-raised exceptions.
The usual `pytest.raises` exception handler is not suitable for errors
raised inside a callback to PETSc because the error is wrapped inside a
`PETSc.Error` object and so this context manager unpacks this to access
the actual internal error.
Parameters
----------
exc_type :
The exception type that is expected to be raised inside a PETSc callback.
"""
def __init__(self, exc_type):
self.exc_type = exc_type

def __enter__(self):
pass

def __exit__(self, exc_type, exc_val, traceback):
if exc_type is PETSc.Error and isinstance(exc_val.__cause__, self.exc_type):
return True


@pytest.fixture
def petsc_raises():
# This function is needed because pytest does not support classes as fixtures.
return _petsc_raises
8 changes: 4 additions & 4 deletions tests/macro/test_macro_multigrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def test_macro_grid_transfer(hierarchy, space, degrees, variant, transfer_type):


@pytest.mark.parametrize("degree", (1,))
def test_macro_multigrid_poisson(hierarchy, degree, variant):
def test_macro_multigrid_poisson(hierarchy, degree, variant, petsc_raises):
mesh = hierarchy[-1]
V = FunctionSpace(mesh, "CG", degree, variant=variant)
u = TrialFunction(V)
Expand All @@ -153,7 +153,7 @@ def test_macro_multigrid_poisson(hierarchy, degree, variant):
problem = LinearVariationalProblem(a, L, uh, bcs=bcs)
solver = LinearVariationalSolver(problem, solver_parameters=mg_params)
if complex_mode and variant == "alfeld":
with pytest.raises(NotImplementedError):
with petsc_raises(NotImplementedError):
solver.solve()
else:
solver.solve()
Expand All @@ -172,7 +172,7 @@ def square_hierarchy():


@pytest.mark.parametrize("family", ("HCT-red", "HCT"))
def test_macro_multigrid_biharmonic(square_hierarchy, family):
def test_macro_multigrid_biharmonic(square_hierarchy, family, petsc_raises):
mesh = square_hierarchy[-1]
V = FunctionSpace(mesh, family, 3)
u = TrialFunction(V)
Expand All @@ -185,7 +185,7 @@ def test_macro_multigrid_biharmonic(square_hierarchy, family):
problem = LinearVariationalProblem(a, L, uh, bcs=bcs)
solver = LinearVariationalSolver(problem, solver_parameters=mg_params)
if complex_mode:
with pytest.raises(NotImplementedError):
with petsc_raises(NotImplementedError):
solver.solve()
else:
solver.solve()
Expand Down
15 changes: 3 additions & 12 deletions tests/slate/test_slate_hybridization.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def test_slate_hybridization(degree, hdiv_family, quadrilateral):
assert u_err < 1e-11


def test_slate_hybridization_wrong_option(setup_poisson):
def test_slate_hybridization_wrong_option(setup_poisson, petsc_raises):
a, L, W = setup_poisson

w = Function(W)
Expand All @@ -145,18 +145,9 @@ def test_slate_hybridization_wrong_option(setup_poisson):
'pc_fieldsplit_type': 'frog'}}}
problem = LinearVariationalProblem(a, L, w)
solver = LinearVariationalSolver(problem, solver_parameters=params)
with pytest.raises(ValueError):
# HybridizationPC isn't called directly from the Python interpreter,
# it's a callback that PETSc calls. This means that the call stack from pytest
# down to HybridizationPC goes via PETSc C code, which interferes with the exception
# before it is observed outside. Hence removing PETSc's error handler
# makes the problem go away, because PETSc stops interfering.
# We need to repush the error handler because popErrorHandler globally changes
# the system state for all future tests.
from firedrake.petsc import PETSc
PETSc.Sys.pushErrorHandler("ignore")

with petsc_raises(ValueError):
solver.solve()
PETSc.Sys.popErrorHandler("ignore")


def test_slate_hybridization_nested_schur(setup_poisson):
Expand Down

0 comments on commit ecc6886

Please sign in to comment.